In-battle skill depending on remaining MP

ShiningRomantica

Villager
Member
Joined
Mar 9, 2020
Messages
18
Reaction score
1
First Language
Portuguese
Primarily Uses
RMMV
Hey!

I need some help with a project of mine... I want the battle system to be very simple, so all of the characteres will have only 3 skills each. I want those skills to be selected automatically when choosing the 'skill' option in the battle menu depending on the remaining MP, instead of choosing it from a list. Is there a script that does it, or would it be doable to be made?

Also, if there's a script that allows at least 1 skill to be used like this, it would help me a lot as well. I just don't want that skill list menu to be shown during battle.

Thanks in advance!
 

theartofme

Villager
Member
Joined
Feb 21, 2019
Messages
21
Reaction score
31
First Language
English
Primarily Uses
RMMV
I have a plugin I developed for a project that might solve your problem. The plugin was designed because I didn't want the standard attack/defend options and I wanted to be able to put any options where I wanted them, but it's general-purpose enough that it can do what you want.

To accomplish what you want with this plugin loaded, you'll want to put the following notetags in either your class or character notes:
Code:
<battlecommand: clear skill>
<battlecommand: add skill 12 eval >
if (a.mp >= 40) show = true;
</battlecommand>
<battlecommand: add skill 13 eval >
if (a.mp < 40 && a.mp >= 20) show = true;
</battlecommand>
<battlecommand: add skill 14 eval >
if (a.mp < 20) show = true;
</battlecommand>
Description:
- "clear skill" removes all the skill menus from this character.
- "add skill 12" adds an action that directly uses skill 12; it doesn't show the skill menu.
- "eval" enables using javascript to decide whether or not to show this menu item, because the built-in logic was based around currently-equipped items and states so it can't handle checking mp.

Altogether, it removes existing skill menus, and adds either skill #12, #13 or #14 to the menu depending on your current mp.

Here's the plugin:
Code:
//=============================================================================
// LWP_CustomBattleMenu.js
//=============================================================================
/*:
 * @plugindesc Full control of the action menu in battles.
 * @author Logan Pickup
 *
 * @param Enable Debugging Info
 * @desc Whether to include debugging log information and unit tests.
 * Remember to turn this OFF for release! on/off/true/false 
 * @default off
 *
 * @param Max Commands
 * @desc The maximum number of commands to display in the command list.
 * @default 4
 * 
 * @help
 * This is a combination of YEP's Weapon Unleash (which allows replacing
 * attack and guard with custom skills) and HIME's Actor Battle Command (
 * which allows customising which battle commands are shown), taken to the
 * next logical level: you can specify a custom list of actions as the
 * battle commands, including changing the order.
 * 
 * Battle commands can be specified on classes, actors, skills, armour,
 * weapons and states. Commands from classes are applied first, then actors,
 * then armour, etc. Later commands override earlier commands (especially if
 * command menu limits are enforced), and if the <battlecommand: no-defaults>
 * or <battlecommand: clear> tags are used then they remove commands that
 * may have been defined already.
 * 
 * If you don't specify visible and enabled states for the menu items, by
 * default they will use the mp/tp calculations to see if they're usable.
 * 
 * Battle commands appear in the menu in the order they are defined. This
 * means commands defined in states, etc. will mostly appear at the bottom;
 * if you want to override this order, you can specify position:x on any
 * battlecommand notetag to influence its position. Higher position values
 * make the command appear later in the list; by default the position starts
 * at 1 and goes up with every command added. You can set fractional position
 * values if you want. 
 * 
 * Changing the name of the commands isn't supported yet, but if you change
 * the name of the attack skill it will show the new attack skill's name in
 * the command list instead of "attack" (same with "guard").
 * 
 * ============================================================================
 * Symbols
 * ============================================================================
 * 
 * The battle menus are defined by "symbols". Each symbol represents a different
 * type of command, and may ahave an additional parameter: "ext", which gives
 * it more info, if needed, to carry out the command. Not all commands need the
 * "ext" parameter; for example, the default attack command has the symbol
 * "attack" and no ext; but the skill menus have the symbol "skill" and the
 * ext must be the skill type to show a menu for.
 * 
 * The default symbols are below:
 * "attack" - makes a default attack.
 * "guard" - performs a guard action.
 * "skill" - shows the skill menu for a type of skill. The skill type to show
 *           is in the "ext" parameter.
 * "item" - shows the item menu to allow an item to be selected and used.
 * 
 * Additional symbols added by this plugin are:
 * "skill_id" - performs a skill immediately, without needing to show the skill
 *              menu. The skill to perform in in the "ext" parameter.
 * "equip" - opens the weapon or armour menu to allow swapping weapon/armour.
 *           The "ext" parameter will be set to "weapon" or "armor".
 * 
 * ============================================================================
 * Notetags
 * ============================================================================
 * 
 * <battlecommand: no-defaults>
 *   Removes the default battle commands, but not any custom battle commands
 *   (even if a custom command is the same as a default command).
 * 
 * <battlecommand: clear [all|symbol] [ext:x]>
 *   Either removes all battle commands (both default and custom) defined up
 *   until this point, or removes a specific battle command by symbol and ext.
 *   If ext isn't specified, it will remove all instances of the <symbol>.
 *   
 * <battlecommand: add attack [position:x]>
 *   Adds a default attack, optionally overriding the default position.
 *   
 * <battlecommand: add guard [position:x]>
 *   Adds a default guard, optionally overriding the default position.
 *   
 * <battlecommand: add item [position:x]>
 *   Adds the default item menu, optionally overriding the default position.
 * 
 * <battlecommand: add skills [position:x]>
 *   Adds all available skill types for the actor, optionally overriding the
 *   default position. The first skill type will get the exact position value
 *   specified, and the following types will get position + 0.1, position +
 *   0.2, etc.
 *   
 * <battlecommand: add skill type [id] [position:x]>
 *   Adds a menu for skills of the specified type, optionally overriding the
 *   default position.
 *  
 * <battlecommand: add skill [id] [position:x]>
 *   Adds a command to trigger a single skill, optionally overriding the
 *   default position. If you want to rename the default attack, this is
 *   one way to do it, by just using the attack skill. Another way is
 *   using Yanfly's WEapon Unleash plugin, which adds the code to get
 *   the default attack to use the name of the skill instead of a hardcoded
 *   name. Ditto for renaming guard.
 *  
 * <battlecommand: add equip weapon [position:x]>
 *   Adds a command to switch weapons, optionally overriding the default
 *   position.
 *  
 * <battlecommand: add equip armor [position:x]>
 *   Adds a command to switch armour, optionally overriding the  default
 *   position.
 * 
 * ============================================================================
 * Advanced Notetags
 * ============================================================================
 * 
 * By putting "if" at the end of any of the <battlecommand> notetags, you can
 * use some of the built-in filtering logic to only show commands when certain
 * conditions are met. The available filters are below:
 * 
 * Left side variables:
 *   wtype: the weapon types of all the currently-weilded weapons.
 *   weapon: the weapon ids of all the currently-weilded weapons.
 *   atype: the armour types of all the currently-equipped armour.
 *   armor: the armour ids of all the currently-equipped armour.
 *   state: the state ids of all current states.
 *   vX: the variable with the id <x>.
 * 
 * Operators:
 *   ==: check passes if at least one value from the left side matches at
 *       least one value from the right side.
 *   =:  same as ==.
 *   !=: check passes if all values from the left side do not match any
 *       values from the right side.
 * 
 * Right side variables:
 *   x:  Any integer value is allowed on the right side.
 *   vX: Any variable is allowed on the right side.
 *   x,vX,x...: Lists of values and/or variables are allowed on the right
 *              side. The list may be all values, all variables, or a
 *              combination of the two. There must be no spaces between the
 *              commas and the values/variables.
 * 
 * The "if" condition can be used like this:
 * <battlecommand add skill 4 if "wtype==14">
 *  - skill 4 is added to the battle menu if the actor is using a weapon with
 *    weapon type 14.
 * 
 * Example with lists of values:
 * <battlecommand add skill 4 if "wtype==14,15,16">
 *  - skill 4 is added to the battle menu if the actor is using a weapon with
 *    weapon type of either 14, 15 or 16.
 * 
 * Example with variables:
 * <battlecommand add skill 4 if "v2==v3">
 *  - skill 4 is added to the battle menu if variable 2 is equal to variable 3.
 * 
 * Alternatively, you can specify switches:
 *   sX: The switch with id <x>. The result of the check is the value of the
 *       switch. example:
 * <battlecommand add skill 4 if "s2">
 *  - skill 4 is added to the battle menu if switch 2 is on.
 * 
 *   not sX: The opposite of the switch with id <x>. The result of the check
 *           is the negated value of the switch. example:
 * <battlecommand add skill 4 if "not s2">
 *  - skill 4 is added to the battle menu if switch 2 is off.
 * 
 * You can combine multiple conditions using "and" and "or". For example:
 * <battlecommand add skill 4 if "wtype==14 and s2">
 *  - skill 4 is added to the battle menu if the actor is using a weapon with
 *    weapon type of 14 and switch 2 is on.
 * 
 * ============================================================================
 * Custom Logic in Notetags
 * ============================================================================
 * 
 * If the built-in conditions are not enough, by putting "eval" at the end of
 * any of the <battlecommand> notetags, you can add custom logic to determine
 * if the notetag should be considered or not by setting the "show" variable,
 * for example:
 * 
 * <battlecommand add skill 4 eval>
 * if (user.weapons()[0].wtypeId === 3) {
 *   show = true;
 * }
 * </battlecommand>
 * 
 * "show" defaults to false. "eval" cannot be mixed with "if". The following
 * variables are available in adition to the standard RPG maker globals:
 * v(X) - get the value of variable number X.
 * s(X) - get the value of switch number X.
 * a - the current actor (always the actor whose battle menu we are setting).
 * user - same as a, the current actor.
 * actor - same as a.
 * skill - if the current menu item refers to a skill, this is that skill.
 */
(function() {
    function booleanParameter(param, defaultValue) {
        if (!LWP.isDefined(param) || param.trim().length === 0) {
            return defaultValue;
        } else {
            return param.toLowerCase() === 'true' || param.toLowerCase() === 'on';
        }
    }
    var parameters = new LWP.Parameters('LWP_CustomBattleMenu');
    var debugEnabled = parameters.getBoolean('Enable Debugging Info', false);
    var globalMaxBattleCommands = parameters.getInteger('Max Commands', 4);
    var logger = LWP.logger('LWP_CustomBattleMenu');
    LWP.onDatabaseLoaded(function() {
        processNotetags(this, $dataClasses);
        processNotetags(this, $dataActors);
        processNotetags(this, $dataArmors);
        processNotetags(this, $dataWeapons);
        processNotetags(this, $dataStates);
    });
    function LWP_BattleCommand(command, subCommand, ext, position, evalLogic, ifConditions) {
        this.command = command;
        this.subCommand = subCommand;
        this.ext = ext;
        this.position = position;
        this.evalLogic = evalLogic;
        this.ifConditions = ifConditions;
    }
    LWP_BattleCommand.prototype.isNoDefaults = function() {
        return this.command === 'no-defaults';
    }
    LWP_BattleCommand.prototype.isClear = function() {
        return this.command === 'clear';
    }
    LWP_BattleCommand.prototype.isAdd = function() {
        return this.command === 'add';
    }
    LWP_BattleCommand.prototype.isSkillList = function() {
        return this.isAdd() && this.subCommand === 'skills';
    }
    LWP_BattleCommand.prototype.isClearAll = function() {
        return this.isClear() && this.subCommand === 'all';
    }
    function processNotetags(dataManager, group) {
        for (var i = 1; i < group.length; i++) {
            var obj = group[i];
            var notedata = obj.note.toLowerCase();
            var re = /<battlecommand:\s*(add|clear|no-defaults)(?:\s+(\w+|all|attack|guard|item|skills|skill type|skill|equip weapon|equip armou?r)(?!\w)(?:\s+(\d+))?(?:\s+position:\s+([-0-9.]+))?)?(?: (if\s+(?:"[^"]+"|'[^']+')|eval))?>/gi
            var match;
            obj.battleCommands = [];
            while (match = re.exec(notedata)) {
                var command = match[1] ? match[1] : match[1];
                var subCommand = match[2] ? match[2] : match[2];
                var idOrExt = match[3] ? Number(match[3]) : match[3];
                var position = match[4] ? Number(match[4]) : match[4];
                var evalLogic = null;
                if (match[5] === 'eval') {
                    var closingTagIndex = notedata.indexOf('</battlecommand>', re.lastIndex);
                    if (closingTagIndex === -1) {
                        logger.error("Error: no closing tag found for " + match[0] + " in " + obj.name);
                    } else {
                        evalLogic = notedata.substring(re.lastIndex, closingTagIndex).trim();
                        re.lastIndex = closingTagIndex + '</battlecommand>'.length;
                    }
                }
                var ifConditions = null;
                if (match[5] && match[5].startsWith('if')) {
                    var expression = match[5].substring(2, match[5].length - 1);
                    expression = expression.trim().substring(1);
                    ifConditions = parseIfConditions(expression);
                }
                var battleCommand = new LWP_BattleCommand(command, subCommand, idOrExt, position, evalLogic, ifConditions);
                obj.battleCommands.push(battleCommand);
            }
        };
    }
    Scene_Battle.prototype.createActorCommandWindow = Scene_Battle.prototype.createActorCommandWindow.wrap(function(createActorCommandWindow) {
        createActorCommandWindow();
        this._actorCommandWindow.setHandler('skill_id', this.commandUseSkill.bind(this));
        this._actorCommandWindow.setHandler('equip', this.commandEquip.bind(this));
    });
    Scene_Battle.prototype.commandUseSkill = function() {
        var skillId = this._actorCommandWindow.currentExt();
        var action = BattleManager.inputtingAction();
        action.setSkill(skillId);
        BattleManager.actor().setLastBattleSkill($dataSkills[skillId]);
        this.onSelectAction();
    }
    Scene_Battle.prototype.commandEquip = function() {
        if (this._actorCommandWindow.currentExt() === 'weapon') {
            // equip weapon
            throw "TODO";
        } else if (this._actorCommandWindow.currentExt() === 'armor') {
            // equip armour
            throw "TODO";
        } else {
            logger.error("Unknown equip type " + this._actorCommandWindow.currentExt());
        }
    }
    function getBattleCommandsFromObject(dataObject) {
        return dataObject.battleCommands || [];
    }
    function getBattleCommandsFromArray(dataObjects) {
        var battleCommands = [];
        for (obj in dataObjects) {
            battleCommands = battleCommands.concat(getBattleCommandsFromObject(obj));
        }
        return battleCommands;
    }
    function findCommands(list, symbol, ext) {
        var indexes = [];
        for (var i = 0; i < list.length; ++i) {
            if (list[i].symbol === symbol) {
                if (!LWP.isDefined(ext) || ext === list[i].ext) {
                    indexes.push(i);
                }
            }
        }
        return indexes;
    }
    function findCommand(list, symbol, ext) {
        for (var i = 0; i < list.length; ++i) {
            if (list[i].symbol === symbol) {
                if (!LWP.isDefined(ext) || ext === list[i].ext) {
                    return i;
                }
            }
        }
        return -1;
    }
    function getBattleCommandsForActor(actor) {
        var battleCommands = [];
        battleCommands = battleCommands.concat(getBattleCommandsFromObject(actor.currentClass()));
        battleCommands = battleCommands.concat(getBattleCommandsFromObject(actor.actor()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.skills()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.armors()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.weapons()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.states()));
        return battleCommands;
    }
    function LWP_CommandListItem(symbol, ext, position, isDefault) {
        this.symbol = symbol;
        this.ext = ext;
        this.position = position;
        this.isDefault = isDefault;
    }
    function replaceOrAdd(commandList, command, forcePosition) {
        if (!command) {
            return;
        }
        var existing = findCommand(commandList, command.symbol, command.ext);
        if (existing != -1) {
            if (forcePosition) {
                commandList[existing].position = command.position;
            }
            commandList[existing].isDefault = commandList[existing].isDefault && command.isDefault;
        } else {
            commandList.push(command);
        }
    }
    function addSkillTypes(commandList, actor, position, isDefault) {
        var currentPosition = LWP.isDefined(position) ? position : commandList.length;
        var skillTypes = actor.addedSkillTypes();
        skillTypes.sort(function(a, b) {
            return a - b;
        });
        skillTypes.forEach(function(sTypeId) {
            var command = new LWP_CommandListItem('skill', sTypeId, currentPosition, isDefault);
            replaceOrAdd(commandList, command, LWP.isDefined(position));
            currentPosition += 0.1;
        }, this);
        return commandList;
    }
    function translateBattleCommand(battleCommand, actor, defaultPosition, isDefault) {
        var ext = battleCommand.ext;
        var position = LWP.isDefined(battleCommand.position) ? battleCommand.position : defaultPosition;
        switch (battleCommand.subCommand) {
            case 'attack':
                return new LWP_CommandListItem('attack', null, position, isDefault);
            case 'guard':
                return new LWP_CommandListItem('guard', null, position, isDefault);
            case 'item':
                return new LWP_CommandListItem('item', null, position, isDefault);
            case 'skill type':
                if (actor.addedSkillTypes().indexOf(ext) !== -1) {
                    return new LWP_CommandListItem('skill', ext, position, isDefault);
                } else {
                    logger.error("Error: attempt to add skill type " + ext + " to actor " + actor.name() + " who doesn't have that skill type");
                    logger.debug(actor);
                    return null;
                }
            case 'skill':
                if (actor.skills().map(function(x) {return x.id}).indexOf(ext) !== -1) {
                    return new LWP_CommandListItem('skill_id', ext, position, isDefault);
                } else {
                    // not necessarily an error, as this can be a way to insert specific skills high
                    // in the list if an actor happens to know them
                    logger.warning("Warning: attempt to add skill " + ext + " to actor " + actor.name() + " who doesn't know that skill");
                    logger.debug(actor.skills());
                    return null;
                }
            case 'equip weapon':
                return new LWP_CommandListItem('equip', 'weapon', position, isDefault);
            case 'equip armor':
            case 'equip armour':
                return new LWP_CommandListItem('equip', 'armor', position, isDefault);
            default:
                logger.error("Error: unhandled command " + battleCommand.command + ":" + battleCommand.subCommand);
                return null;
        }
    }
    function applyBattleCommand(commandList, battleCommand, actor) {
        var position = LWP.isDefined(battleCommand.position) ? battleCommand.position : commandList.length;
        if (battleCommand.isNoDefaults()) {
            return commandList.filter(function(command) {!command.isDefault;});
        } else if (battleCommand.isClearAll()) {
            return [];
        } else if (battleCommand.isClear()) {
            var found = findCommands(commandList, battleCommand.subCommand, battleCommand.ext);
            // process the indexes in reverse, otherwise the later indexes will change as the list
            // is altered
            found.reverse().forEach(function(index) {
                commandList.splice(index, 1);
            });
            return commandList;
        } else if (battleCommand.isAdd()) {
            if (battleCommand.isSkillList()) {
                return addSkillTypes(commandList, actor, battleCommand.position, false);
            } else {
                var newCommand = translateBattleCommand(battleCommand, actor, position, false);
                replaceOrAdd(commandList, newCommand, LWP.isDefined(battleCommand.position));
                return commandList;
            }
        } else {
            logger.error("Error: unhandled command " + battleCommand.command + ":" + battleCommand.subCommand);
            return commandList;
        }
    }
    function addDefaultCommands(commandList, actor) {
        commandList.push(new LWP_CommandListItem('attack', null, commandList.length, true));
        commandList = addSkillTypes(commandList, actor, null, true);
        commandList.push(new LWP_CommandListItem('guard', null, commandList.length, true));
        commandList.push(new LWP_CommandListItem('item', null, commandList.length, true));
        return commandList;
    }
    function filteredBattleCommands(battleCommands, actor) {
        return battleCommands.filter(function(command) {
            if (command.evalLogic) {
                // globals meant to be commonly available to evals
                var v = function(x) {return $gameVariables.value(x);};
                var s = function(x) {return $gameSwitches.value(x);};
                var a = actor;
                var user = actor;
                var show = false;
                var skill = actor.skills().filter(function(x) {return x.id === command.ext});
                var skill = skill.length > 0 ? skill[0] : null;
                eval(command.evalLogic);
                return show;
            } else if (command.ifConditions) {
                return command.ifConditions(actor);
            } else {
                return true;
            }
        });
    }
    Window_ActorCommand.prototype.maxBattleCommands = function() {
        return globalMaxBattleCommands;
    }
    Window_ActorCommand.prototype.makeCommandList = Window_ActorCommand.prototype.makeCommandList.wrap(function(makeCommandList) {
        if (this._actor) {
            var actor = this._actor;
            // get the raw command definitions
            var battleCommands = getBattleCommandsForActor(actor);
            battleCommands = filteredBattleCommands(battleCommands, actor);
            if (battleCommands.length == 0) {
                // early out if there are no battle commands defined
                makeCommandList();
                return;
            }
            logger.debug("Battle commands:");
            logger.debug(battleCommands);
            var commandList = [];
            commandList = addDefaultCommands(commandList, actor);
            logger.debug("Command list - default commands:");
            logger.debug(commandList);
            // put them in an intermediate format, and apply clears/overrides
            battleCommands.forEach(function(command) {
                commandList = applyBattleCommand(commandList, command, actor);
            });
            logger.debug("Command list - after adding commands:");
            logger.debug(commandList);
            // sort according to position
            commandList.sort(function(a, b) {
                return a.position - b.position;
            });
            logger.debug("Command list - after sorting:");
            logger.debug(commandList);
            // limit command list length
            commandList = commandList.slice(0, this.maxBattleCommands());
            logger.debug("Command list - after truncating to maximum length:");
            logger.debug(commandList);
            // add the commands to the menu
            var self = this;
            commandList.forEach(function(command) {
                if (command.symbol === 'attack') {
                    self.addAttackCommand();
                } else if (command.symbol === 'guard') {
                    self.addGuardCommand();
                } else if (command.symbol === 'item') {
                    self.addItemCommand();
                } else if (command.symbol === 'skill') {
                    var name = $dataSystem.skillTypes[command.ext];
                    self.addCommand(name, command.symbol, true, command.ext);
                } else if (command.symbol === 'skill_id') {
                    var skill = $dataSkills[command.ext];
                    var name = skill.commandAttackText
                    var enabled = actor.meetsSkillConditions(skill);
                    self.addCommand(name, command.symbol, enabled, command.ext);
                } else if (command.symbol === 'equip') {
                    var enabled = false;
                    self.addCommand(command.ext, command.symbol, enabled, command.ext);
                }
            });
        } else {
            makeCommandList();
        }
    });
    //////////////////////////////////////////////////////////////////
    // Expression Parser
    //
    // Note that this hasn't been well tested; only a few combinations
    // are known for certain to work. It's proably OK though! :)
    //////////////////////////////////////////////////////////////////
    // parse tree:
    // or-expression-part: and-expresion | simple-expression
    // or-expression: or-expression-part "or" or-expression-part [ "or" or-expression-part ...]
    // and-expression-part: simple-expression
    // and-expression: and-expression-part "and" and-expression-part [ "and" and-expression-part ...]
    // simple-expression: variable-expression | switch-expression
    // switch-expression: [not] s + integer
    // variable-expression: variable operator value-list
    // variable: actor-prop | variable-id
    // operator: "==" | "=" | "!="
    // value-list: value[,value...] (note: no spaces allowed in this list because reasons (i.e. I'm lazy))
    // value: integer | variable-id
    // variable-id: "v" + integer
    function parseIfConditions(expression) {
        return parseOr(expression, expression);
    }
    function parseOr(subexpression, expression) {
        var orClauses = subexpression.split(/\s+or\s+/);
        return combineOrClauses(orClauses.map(function(orClause) {
            return parseAnd(orClause, expression);
        }));
    }
    function parseAnd(subexpression, expression) {
        var andClauses = subexpression.split(/\s+and\s+/);
        return combineAndClauses(andClauses.map(function(andClause) {
            return parseSimpleExpression(andClause, expression);
        }));
    }
    function combineOrClauses(booleanFunctions) {
        return function(actor) {
            var result = false;
            logger.debug("OR (" + booleanFunctions.length + ")");
            booleanFunctions.forEach(function(fn) {
                result = result || fn(actor);
                logger.debug(result);
            });
            return result;
        }
    }
    function combineAndClauses(booleanFunctions) {
        return function(actor) {
            var result = true;
            logger.debug("AND (" + booleanFunctions.length + ")");
            booleanFunctions.forEach(function(fn) {
                result = result && fn(actor);
                logger.debug(result);
            });
            return result;
        }
    }
    
    var variableAccessors = {
        'wtype': function(actor) {
            return actor.weapons().map(function(x) {return x.wtypeId;});
        },
        'weapon': function(actor) {
            return actor.weapons().map(function(x) {return x.baseItemId;});
        },
        'armor': function(actor) {
            return actor.armors().map(function(x) {return x.baseItemId;});
        },
        'atype': function(actor) {
            return actor.armors().map(function(x) {return x.atypeId;});
        },
        'state': function(actor) {
            return actor.states().map(function(x) {return x.id;});
        },
    };
    function variableAccessor(variableExpression) {
        var variableId = +variableExpression.substring(1);
        return function() {return $gameVariables.value(variableId);};
    }
    function parseSimpleExpression(expression, context) {
        var re = /\s*([a-z0-9]+)\s*(==?|!=)\s*([v0-9,]+)\s*|\s*(?:(not) )?s([0-9]+)\s*/i;
        var parts = re.exec(expression);
        if (parts && parts[1]) {
            var variable = parts[1];
            var operator = parts[2];
            var valueGenerators = parts[3].split(",").map(function(x) {
                if (x.startsWith('v')) {
                    return variableAccessor(x);
                } else {
                    return function() {return +x;};
                }
            });
            var accessor = variableAccessors[variable];
            if (!accessor && /v[0-9]+/.test(variable)) {
                accessor = variableAccessor(variable);
            }
            if (!accessor) {
                logger.error("Error: no accessor found for variable " + variable + " in expression " + expression + " in: " + context);
                return function() {return false;};
            }
            return function(actor) {
                logger.debug("actor:", actor);
                var lValues = accessor(actor);
                if (!LWP.isDefined(lValues.length)) {
                    lValues = [lValues];
                }
                var values = valueGenerators.map(function(x) {return x();});
                logger.debug(expression);
                logger.debug(lValues, values);
                if (operator === '==' || operator === '=') {
                    return lValues
                        .map(function(l) { return values.indexOf(l) !== -1; })
                        .reduce(function(previous, current) {return previous || current}, false);
                } else if (operator === '!=') {
                    return lValues
                        .map(function(l) { return values.indexOf(l) === -1; })
                        .reduce(function(previous, current) {return previous && current}, true);
                } else {
                    logger.error("Error: unhandled operator " + operator + " in " + expression + " in: " + context);
                    return false;
                }
            };
        } else if (parts && parts[5]) {
            var not = !!parts[4];
            var switchId = +parts[5];
            if (not) {
                return function() {
                    logger.debug("not switch", $gameSwitches.value(switchId));
                    return !$gameSwitches.value(switchId);
                };
            } else {
                return function() {
                    logger.debug("switch", $gameSwitches.value(switchId));
                    return $gameSwitches.value(switchId);
                };
            }
        } else {
            logger.error("Error: malformed expression " + expression + " in: " + context);
            return function() {return false;};
        }
    }
    ////////////////// TESTS ///////////////////
    DataManager.createGameObjects = DataManager.createGameObjects.wrap(function(createGameObjects) {
        createGameObjects();
        if (debugEnabled) {
            logger.setLogLevel(LWP.LOG_VERBOSE);
            expressionParserTests();
        }
    });
    function expressionParserTests() {
        logger.info("Tests starting");
        testActorPropWeaponType();
        testActorPropWeaponId();
        testActorPropArmorType();
        testActorPropArmorId();
        testActorPropState();
        testActorPropEqualsExpressionTrue();
        testActorPropEqualsExpressionFalse();
        testActorPropNotEqualsExpressionTrue();
        testActorPropNotEqualsExpressionFalse();
        testActorPropEqualsVariableTrue();
        testActorPropEqualsVariableFalse();
        testActorPropNotEqualsVariableTrue();
        testActorPropNotEqualsVariableFalse();
        testActorPropEqualsListTrue();
        testActorPropEqualsListFalse();
        testActorPropNotEqualsListTrue();
        testActorPropNotEqualsListFalse();
        testActorMultiPropSomeInListEqualsTrue();
        testActorMultiPropAllInListEqualsTrue();
        testActorMultiPropAllNotInListEqualsFalse();
        testActorMultiPropAllNotInListNotEqualsTrue();
        testActorMultiPropSomeInListNotEqualsFalse();
        testActorMultiPropAllInListNotEqualsFalse();
        testActorMultiPropSomeInListWithVariablesEqualsTrue();
        testVariableEqualsValue();
        testVariableEqualsVariable();
        testVariableEqualsList();
        testSwitchOn();
        testSwitchOff();
        testSwitchNotOn();
        testSwitchNotOff();
        testEqualsTrueAndEqualsTrue();
        testEqualsTrueAndEqualsFalse();
        testEqualsFalseAndEqualsTrue();
        testEqualsFalseAndEqualsFalse();
        testEqualsTrueAndSwitchTrue();
        testEqualsTrueAndNotSwitchTrue();
        testEqualsTrueOrEqualsTrue();
        testEqualsTrueOrEqualsFalse();
        testEqualsFalseOrEqualsTrue();
        testEqualsFalseOrEqualsFalse();
        testEqualsFalseOrSwitchTrue();
        testEqualsTrueOrNotSwitchFalse();
        testAndPrecedenceHigherThanOr();
        testLongAndOrChain();
        logger.info("Tests finished");
    }
    function testActorPropWeaponType() {
        givenTheExpression("wtype==1")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropWeaponId() {
        givenTheExpression("weapon==3")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropArmorType() {
        givenTheExpression("atype==1")
            .andAnActorWith().armor({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropArmorId() {
        givenTheExpression("armor==3")
            .andAnActorWith().armor({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropState() {
        givenTheExpression("state==3")
            .andAnActorWith().state(3)
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsExpressionTrue() {
        givenTheExpression("wtype==1")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsExpressionFalse() {
        givenTheExpression("wtype==1")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropNotEqualsExpressionTrue() {
        givenTheExpression("wtype!=1")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropNotEqualsExpressionFalse() {
        givenTheExpression("wtype!=1")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropEqualsVariableTrue() {
        givenTheExpression("wtype==v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsVariableFalse() {
        givenTheExpression("wtype==v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropNotEqualsVariableTrue() {
        givenTheExpression("wtype!=v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropNotEqualsVariableFalse() {
        givenTheExpression("wtype!=v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropEqualsListTrue() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsListFalse() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith().weapon({id: 3, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropNotEqualsListTrue() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith().weapon({id: 3, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropNotEqualsListFalse() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropSomeInListEqualsTrue() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorMultiPropAllInListEqualsTrue() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 2})
                .weapon({id: 3, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorMultiPropAllNotInListEqualsFalse() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 5})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropAllNotInListNotEqualsTrue() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 5})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorMultiPropSomeInListNotEqualsFalse() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 2})
                .weapon({id: 3, type: 5})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropAllInListNotEqualsFalse() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 2})
                .weapon({id: 3, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropSomeInListWithVariablesEqualsTrue() {
        givenTheExpression("wtype==v1,v2,v3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 6})
            .andAVariable({id: 1, value: 5})
            .andAVariable({id: 2, value: 5})
            .andAVariable({id: 3, value: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    // At this point, we've fairly exhaustively testing the equals and not equals
    // operators. We'll skip every single variation, and focus on different
    // combinations of left- and right-hand values.
    function testVariableEqualsValue() {
        givenTheExpression("v1==3")
            .andAVariable({id: 1, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testVariableEqualsVariable() {
        givenTheExpression("v1==v2")
            .andAVariable({id: 1, value: 3})
            .andAVariable({id: 2, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testVariableEqualsList() {
        givenTheExpression("v1==2,3,4")
            .andAVariable({id: 1, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testSwitchOn() {
        givenTheExpression("s3")
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testSwitchOff() {
        givenTheExpression("s3")
            .andASwitch({id: 3, value: false})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testSwitchNotOn() {
        givenTheExpression("not s3")
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testSwitchNotOff() {
        givenTheExpression("not s3")
            .andASwitch({id: 3, value: false})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    // and/or
    function testEqualsTrueAndEqualsTrue() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueAndEqualsFalse() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsFalseAndEqualsTrue() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsFalseAndEqualsFalse() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsTrueAndSwitchTrue() {
        givenTheExpression("wtype==4 and s3")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueAndNotSwitchTrue() {
        givenTheExpression("wtype==4 and not s3")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .andASwitch({id: 3, value: false})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueOrEqualsTrue() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueOrEqualsFalse() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsFalseOrEqualsTrue() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsFalseOrEqualsFalse() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsFalseOrSwitchTrue() {
        givenTheExpression("wtype==4 or s3")
            .andAnActorWith()
                .weapon({id: 2, type: 3})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueOrNotSwitchFalse() {
        givenTheExpression("wtype==4 or not s3")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testAndPrecedenceHigherThanOr() {
        givenTheExpression("s3 or v1==2 and v2==3")
            .andAVariable({id: 1, value: 2})
            .andAVariable({id: 2, value: 2})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testLongAndOrChain() {
        givenTheExpression("not s1 or not s2 or s3 and s4 and s5 or not s6 and s7 or not s8")
            .andASwitch({id: 1, value: true})
            .andASwitch({id: 2, value: true})
            .andASwitch({id: 3, value: true})
            .andASwitch({id: 4, value: true})
            .andASwitch({id: 5, value: true})
            .andASwitch({id: 6, value: true})
            .andASwitch({id: 7, value: true})
            .andASwitch({id: 8, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    // Test framework
    function givenTheExpression(expression) {
        var TARGET_ACTOR = 'the actor';
        return {
            tag: "Given an expression \"" + expression + "\"\n",
            currentlyAnnotating: null,
            tagNeedsNewline: false,
            variables: {},
            switches: {},
            actorProps: {
                weapons: [],
                armors: [],
                states: []
            },
            condition: parseIfConditions(expression),
            andAnActorWith: function() {
                this._fixAnnotations(null);
                this.tag += "And an actor with ";
                this.currentlyAnnotating = TARGET_ACTOR;
                this.tagNeedsNewline = true;
                return this;
            },
            weapon: function(data) {
                this._fixAnnotations(TARGET_ACTOR);
                this.tag += "a weapon (id=" + data.id + ", type=" + data.type + ") ";
                this.actorProps.weapons.push(makeWeapon(data.id, data.type));
                return this;
            },
            armor: function(data) {
                this._fixAnnotations(TARGET_ACTOR);
                this.tag += "armour (id=" + data.id + ", type=" + data.type + ") ";
                this.actorProps.armors.push(makeArmor(data.id, data.type));
                return this;
            },
            state: function(id) {
                this._fixAnnotations(TARGET_ACTOR);
                this.tag += "state " + id + " ";
                this.actorProps.states.push({id: id});
                return this;
            },
            andAVariable: function(data) {
                this._fixAnnotations(null);
                this.tag += "And a variable " + data.id + " with the value " + data.value + "\n";
                this.variables[data.id] = data.value;
                return this;
            },
            andASwitch: function(data) {
                this._fixAnnotations(null);
                this.tag += "And a switch " + data.id + " that is " + (data.value ? "on" : "off") + "\n";
                this.switches[data.id] = data.value;
                return this;
            },
            whenTheConditionIsChecked: function() {
                this._fixAnnotations(null);
                this.tag += "When the condition is checked\n";
                this.actor = dummyActor(this.actorProps.weapons, this.actorProps.armors, this.actorProps.states);
                this._setVariables();
                this.checkResult = this.condition(this.actor);
                this._restoreVariables();
                return this;
            },
            theConditionShouldPass: function() {
                this._fixAnnotations(null);
                this.tag += "Then the condition should pass\n";
                this._assert(this.checkResult, this.tag);
                logger.info(this.tag + " - PASSED");
                return this;
            },
            theConditionShouldFail: function() {
                this._fixAnnotations(null);
                this.tag += "Then the condition should fail\n";
                this._assert(!this.checkResult, this.tag);
                logger.info(this.tag + " - PASSED");
                return this;
            },
            _setVariables: function() {
                this.oldVariableValues = {}
                for (var variableId in this.variables) {
                    if (this.variables.hasOwnProperty(variableId)) {
                        this.oldVariableValues = $gameVariables.value(variableId);
                        $gameVariables.setValue(variableId, this.variables[variableId]);
                    }
                }
                this.oldSwitchValues = {}
                for (var switchID in this.switches) {
                    if (this.switches.hasOwnProperty(switchID)) {
                        this.oldSwitchValues = $gameSwitches.value(switchID);
                        $gameSwitches.setValue(switchID, this.switches[switchID]);
                    }
                }
            },
            _restoreVariables: function() {
                for (var variableId in this.variables) {
                    if (this.variables.hasOwnProperty(variableId)) {
                        $gameVariables.setValue(variableId, this.oldVariableValues[variableId]);
                    }
                }
                for (var switchID in this.switches) {
                    if (this.switches.hasOwnProperty(switchID)) {
                        $gameSwitches.setValue(switchID, this.oldSwitchValues[switchID]);
                    }
                }
            },
            _fixAnnotations: function(forAnnotationTarget) {
                if (this.currentlyAnnotating != forAnnotationTarget) {
                    if (this.tagNeedsNewline) {
                        this.tag += '\n';
                        this.tagNeedsNewline = false;
                    }
                    this.currentlyAnnotating = forAnnotationTarget;
                    if (forAnnotationTarget) {
                        this.tag += "And " + forAnnotationTarget + " has ";
                        this.tagNeedsNewline = true;
                    }
                }
            },
            _assert: function(test, message) {
                if (!test) {
                    throw new Error(message);
                }
            }
        };
    }
    function dummyActor(weapons, armors, states) {
        if (!weapons) weapons = [];
        if (!LWP.isDefined(weapons.length)) weapons = [weapons];
        if (!armors) armors = [];
        if (!LWP.isDefined(armors.length)) armors = [armors];
        if (!states) states = [];
        if (!LWP.isDefined(states.length)) states = [states];
        return {
            weapons: function() {return weapons;},
            armors: function() {return armors;},
            states: function() {return states;}
        }
    }
    function makeWeapon(id, type) {
        return {
            baseItemId: id,
            wtypeId: type
        }
    }
    function makeArmor(id, type) {
        return {
            baseItemId: id,
            atypeId: type
        }
    }
})();
 

ShiningRomantica

Villager
Member
Joined
Mar 9, 2020
Messages
18
Reaction score
1
First Language
Portuguese
Primarily Uses
RMMV
From what you described, it seems to be it. Thanks! I'm sure it will help me out, but... I wasn't able to actually use it.

I don't know what I'm doing wrong but it simply won't work. I created a new project to make sure that no conflicts would happen, and I have also removed the default plugins. From your example, I only changed what skills should appear on the battle menu, as well as at how much MP they would appear.

What could I be doing wrong? I tried to put it on the notes first on the actor, then on the class.

01.png

02.png

03.png
 

theartofme

Villager
Member
Joined
Feb 21, 2019
Messages
21
Reaction score
31
First Language
English
Primarily Uses
RMMV
Sorry, you're right! I hadn't used it outside my own project before, and it failed when I put it in a sandbox. It has two dependencies - one on my utility library, and one on YEP_WeaponUnleashed. The one on YEP_WeaponUnleashed was totally unintentional, and I've fixed it. My browser crashes trying to edit my original post, so here is the update:


LPW_Util - must be above LWP_CustomBattleMenu in the plugin list:

Code:
//=============================================================================
// LWP_Util.js
//=============================================================================
/*:
 * @plugindesc Utility methods used in other LWP_* plugins.
 * @author Logan Pickup
 * 
 * @help
 * This plugin does nothing by itself. It provides various utility methods
 * used by other LWP plugins, so must be included in the plugin list above
 * any other LWP plugins present.
 */
if (typeof(LWP) === 'undefined') {
    LWP = {};
}
(function() {
    if (LWP.GRAVITY_TOP) {
        // skip if already initialised
        return;
    }
    LWP.isDefined = isDefined;
    LWP.logger = logger;
    LWP.createCommandWindow = createCommandWindow;
    LWP.onDatabaseLoaded = onDatabaseLoaded;
    LWP.addPluginCommand = addPluginCommand;
    LWP.Parameters = ParameterWrapper;
    LWP.GRAVITY_TOP = 1;
    LWP.GRAVITY_BOTTOM = 2;
    LWP.GRAVITY_VCENTER = 3;
    LWP.GRAVITY_LEFT = 4;
    LWP.GRAVITY_RIGHT = 8;
    LWP.GRAVITY_HCENTER = 12;
    LWP.LOG_VERBOSE = {priority: 0, tag: "V", fn: console.log};
    LWP.LOG_DEBUG = {priority: 1, tag: "D", fn: console.log};
    LWP.LOG_WARN = {priority: 2, tag: "W", fn: console.warn};
    LWP.LOG_ERROR = {priority: 3, tag: "E", fn: console.error};
    var NOOP = function() {};
    var VERTICAL_GRAVITY_MASK = 3;
    var HORIZONTAL_GRAVITY_MASK = 12;
    var internalLogger = logger("LWP_Util");
    function toArray(x) {
        return Array.prototype.slice.call(x);
    }
    function insert(a, pos) {
        var args = toArray(arguments).slice(2);
        a.splice.apply(a, [pos, 0].concat(args));
        return a;
    }
    /**
     * Returns a logger that can be used to log output. This is mostly just a wrapper
     * around the console methods, like console.log and console.debug, but it also
     * contains a log level switch allowing you to choose what the lowest level of
     * logs you are interested in is.
     * 
     * The returned object has the following methods:
     * .v(message) - log a message at LWP.LOG_VERBOSE level
     * .d(message) - log a message at LWP.LOG_DEBUG level
     * .w(message) - log a message at LWP.LOG_WARN level
     * .e(message) - log a message at LWP.LOG_ERROR level
     * .setLogLevel(level) - set the log level for this logger.
     * 
     * @param {*} tag By convention this is the filename (without extension) of the
     *                current file. Another convention is to use the name of the class.
     *                This value will prefix all logging output from the returned logger,
     *                so to save space CamelCase is abbreviated to only the first letter
     *                of each word (i.e. all the capital letters). This abbreviation is
     *                done automatically.
     * @param {*} defaultLevel Optional - the initial log level. Defaults to LWP.LOG_WARN 
     */
    function logger(tag, defaultLevel) {
        var shortTag = tag.replace(/^[A-Z]+_|[a-z]+/g,'');
        function getLogFunction(forLevel, currrentLevel) {
            if (forLevel.priority >= currrentLevel.priority) {
                return forLevel.fn.bind(console, shortTag);
            } else {
                return NOOP;
            }
        }
        var logger = {};
        logger.setLogLevel = function(level) {
            logger.v = getLogFunction(LWP.LOG_VERBOSE, level);
            logger.verbose = logger.v;
            logger.info = logger.v;
            logger.d = getLogFunction(LWP.LOG_DEBUG, level);
            logger.debug = logger.d;
            logger.w = getLogFunction(LWP.LOG_WARN, level);
            logger.warning = logger.w;
            logger.e = getLogFunction(LWP.LOG_ERROR, level);
            logger.error = logger.e;
        };
        logger.setLogLevel(defaultLevel || LWP.LOG_WARN);
        return logger;
    }
    /**
     * Wraps the function in another function.
     * Returns a function that when called, binds the function passed to it correctly
     * so that "this" is bound as expected and the first parameter is the function it
     * was called on.
     * 
     * For example:
     * Array.prototype.splice = Array.prototype.splice.wrap(function(oldSplice, arg1) {
     *      console.log("Array.splice() was called on " + this);
     *      oldSplice(arg1);
     * })
     * 
     * This is useful for chaining overrides of a function, so you can modify parts of
     * it but still call the original implementation.
     */
    Function.prototype.wrap = function(f) {
        var originalFunction = this;
        return function() {
            return f.apply(this, insert(toArray(arguments), 0, originalFunction.bind(this)));
        }
    };
    /**
     * Returns true if the parameter is not undefined or null. 
     */
    function isDefined(x) {
        var undefined;
        return (x !== undefined && x !== null);
    }
//=============================================================================
// Plugin parameter parsing
//=============================================================================
    /**
     * Wraps PluginManager.parameters to provide type-friendly getter methods.
     * @param {*} scriptName The name of your script, without the .js extension.
     */
    function ParameterWrapper(scriptName) {
        this.parameters = PluginManager.parameters(scriptName);
        this.scriptName = scriptName;
    }
    /**
     * Returns a boolean value from a parameter. Treats the strings "true" and
     * "on" as true, and all others as false.
     */
    ParameterWrapper.prototype.getBoolean = function(parameterName, defaultValue) {
        var param = this.parameters[parameterName];
        if (!LWP.isDefined(param) || param.trim().length === 0) {
            return defaultValue;
        } else {
            return param.trim().toLowerCase() === 'true' || param.trim().toLowerCase() === 'on';
        }
    }
    /**
     * Returns a string value from a parameter. Automatically trims trailing and leading
     * spaces.
     */
    ParameterWrapper.prototype.getString = function(parameterName, defaultValue) {
        var param = this.parameters[parameterName];
        if (!LWP.isDefined(param) || param.trim().length === 0) {
            return defaultValue;
        } else {
            return param.trim();
        }
    }
    /**
     * Returns an integer value from a parameter. The parameter may be specified with
     * decimal places, but only the integer part will be returned.
     */
    ParameterWrapper.prototype.getInteger = function(parameterName, defaultValue) {
        var param = this.parameters[parameterName];
        if (!LWP.isDefined(param) || param.trim().length === 0) {
            return defaultValue;
        } else {
            return Math.floor(Number(param));
        }
    }
    /**
     * Gets a parameter as an enum. This takes the parameter name and default values
     * like the other get*() methods, but it also takes a map of parameter values to
     * output values. This is largely unnecessary if you use the "select" type!
     * 
     * The keys of the map should be UPPERCASE (the parameter is converted to
     * uppercase before checking against the map), and if the parameter matches one
     * of the keys, then the corresponing value in the map is returned. If the parameter
     * is empty or the parameter's value can't be foun in the map, then the default
     * value is returned.
     * 
     * Example:
     * 
     * // This emulates getBoolean("test", false)
     * var parameters = new LWP.Parameters("testfile");
     * parameters.getEnum("test", false, {
     *     'TRUE': true,
     *     'ON': true
     * });
     */
    ParameterWrapper.prototype.getEnum = function(parameterName, defaultValue, valuesMap) {
        let upperCaseValues = {};
        Object.keys(valuesMap).forEach(key => upperCaseValues[key.toUpperCase()] = valuesMap[key])
        var param = this.parameters[parameterName];
        if (!LWP.isDefined(param) || param.trim().length === 0) {
            return defaultValue;
        } else {
            var foundValue = upperCaseValues[param.trim().toUpperCase()];
            if (LWP.isDefined(foundValue)) {
                return foundValue;
            } else {
                return defaultValue;
            }
        }
    }
    /**
     * Generic value getter. Simply calls JSON.parse recursively on the parameter
     * value, so it will automatically convert numbers, lists, structures, etc. It
     * will also work for booleans that use true/false as the underlying values.
     * 
     * The default value will NOT be parsed as JSON, it will be returned directly
     * if the parameter is absent or empty.
     */
    ParameterWrapper.prototype.getValue = function(parameterName, defaultValue) {
        var param = this.parameters[parameterName];
        if (!LWP.isDefined(param) || param.trim().length === 0) {
            return defaultValue;
        }
        
        return parseDeep(param);
    };
    /**
     * Returns the underlying parameters object. This can be useful for serialisation
     * or debugging.
     */
    ParameterWrapper.prototype.getRaw = function() {
        return this.parameters;
    };
    // free function; will not be accessible outside this plugin.
    function parseDeep(maybeJsonString) {
        try {
            return JSON.parse(maybeJsonString, function(key, value) {
                try {
                    return parseDeep(value);
                } catch (e) {
                    return value;
                }
            });
        } catch (e) {
            return maybeJsonString;
        }
    }
//=============================================================================
// Windows
//=============================================================================
    /**
     * Allows creating a command window, that contains a list of selections and a handler
     * for each selection, without having to subclass Window_Command yourself. This method
     * can be called with a list of choices, and it will display the choices and call the
     * associated handler when the choice is selected.
     * 
     * As with other windows that subclass Window_Command, if you use "ok" or "cancel" as
     * the symbol for a choice, that choice will become the default if the action button
     * or the cancel button, respectively, are pressed.
     * 
     * @param {*} choices An array of objects describing the menu. Each item in the array
     *                    should be in the format {text: "", symbol: "", handler: function()}
     *                    text is what is displayed in the menu, symbol is only used to
     *                    handle dispatching menu press events to the right handler and
     *                    should be unique within this set of choices (it can also be the
     *                    special values "ok" or "cancel" to indicate they should be the
     *                    default and cancel items respectively), and handler is a method
     *                    that is called when an item is selected (this handler is called
     *                    exactly the same way as a handler set by the Window_Command.setHandler
     *                    method).
     * @param {*} gravity Optional: the side the window should align with. Accepts the
     *                    LPWP.GRAVITY_* constants; one vertical and one horizontal combined
     *                    with the bitwise-or operator. The default if not specified is
     *                    LWP.GRAVITY_BOTTOM | LWP.GRAVITY_RIGHT
     */
    function createCommandWindow(choices, gravity) {
        gravity = gravity || (LWP.GRAVITY_BOTTOM | LWP.GRAVITY_RIGHT);
        var width = 240;
        var x = 0;
        switch (gravity & HORIZONTAL_GRAVITY_MASK) {
            case LWP.GRAVITY_LEFT: x = 0; break;
            case LWP.GRAVITY_RIGHT: x = Graphics.boxWidth - width; break;
            case LWP.GRAVITY_HCENTER: x = (Graphics.boxWidth - width) / 2; break;
        }
        var height = new Window_Command().fittingHeight(choices.length);
        var y = 0;
        switch (gravity & VERTICAL_GRAVITY_MASK) {
            case LWP.GRAVITY_TOP: y = 0; break;
            case LWP.GRAVITY_BOTTOM: y = Graphics.boxHeight - height; break;
            case LWP.GRAVITY_VCENTER: y = (Graphics.boxHeight - height) / 2; break;
        }
        internalLogger.v("selection window from", choices);
        var Window_CommandWithChoices = function() {
            this.initialize.apply(this, arguments);
        }
        Window_CommandWithChoices.prototype = Object.create(Window_Command.prototype);
        Window_CommandWithChoices.prototype.constructor = Window_CommandWithChoices;
        Window_CommandWithChoices.prototype.makeCommandList = function() {
            choices.forEach(function(item) {
                internalLogger.v("adding command " + item.text + "[" + item.symbol + "]");
                this.addCommand(item.text, item.symbol, !!item.handler);
            }.bind(this));
        };
        Window_CommandWithChoices.prototype.windowWidth = function() {
            return width;
        };
        Window_CommandWithChoices.prototype.numVisibleRows = function() {
            return choices.length;
        };
        var window = new Window_CommandWithChoices(x, y);
        choices.forEach(function(item) {
            if (item.handler) {
                window.setHandler(item.symbol, item.handler);
            }
        });
        return window;
    }
    
//=============================================================================
// Plugin hooks
//=============================================================================
    var onDatabaseLoadedCallbacks = [];
    /**
     * Executes the given callback when the databse has been loaded. This is commonly
     * used by plugins in order to begin processing that depends on the database,
     * that should still be done before the rest of the engine starts - this is a good
     * place, for example, to process notetags. The callback is guaranteed to only be
     * called once, too, so there is no need for a "loaded" flag inside the callback.
     * 
     * @param {*} callback The method to call when the database is loaded. The DataManager
     *                     instance will be bound to `this`.
     */
    function onDatabaseLoaded(callback) {
        onDatabaseLoadedCallbacks = onDatabaseLoadedCallbacks.concat([callback]);
    }
    DataManager.isDatabaseLoaded = DataManager.isDatabaseLoaded.wrap(function(isDatabaseLoaded) {
        if (!isDatabaseLoaded()) return false;
        if (onDatabaseLoadedCallbacks.length > 0) {
            var dataManager = this;
            onDatabaseLoadedCallbacks.forEach(function(callback) {
                callback.bind(dataManager)();
            });
            onDatabaseLoadedCallbacks = [];
        }
        return true;
    });
    var pluginCommandHandlers = [];
    /**
     * Adds a plugin command handler. This removes the need to override Game_Interpreter.prototype.pluginCommand
     * yourself.
     * 
     * @param {*} matcher The plugin command to handle.
     * @param {*} handler The method to call when the plugin command specified is given. It will
     *                    be called with the command (string) and the arguments (array).
     */
    function addPluginCommand(matcher, handler) {
        pluginCommandHandlers = pluginCommandHandlers.concat([
            {
                matcher: matcher,
                handler: handler
            }
        ]);
    }
    Game_Interpreter.prototype.pluginCommand = Game_Interpreter.prototype.pluginCommand.wrap(function(pluginCommand, command, args) {
        pluginCommand(command, args);
        interpreter = this;
        for (var i in pluginCommandHandlers) {
            var handler = pluginCommandHandlers[i];
            if (pluginCommandMatches(handler.matcher, command)) {
                handler.handler.bind(interpreter)(command, args);
                break;
            }
        }
    });
    function pluginCommandMatches(matcher, command) {
        if (typeof(matcher) === 'function') {
            return matcher(Command);
        } else if (matcher instanceof RegExp) {
            return matcher.test(command);
        } else if (matcher instanceof Array) {
            return matcher.map(function(subMatcher) {
                pluginCommandMatches(subMatcher, command)
            }).reduce(function(accumulator, current) {
                return accumulator || current;
            }, false);
        } else {
            return matcher === command;
        }
    }
})();


Fixed version of LWP_CustomBattleMenu:

Code:
//=============================================================================
// LWP_CustomBattleMenu.js
//=============================================================================
/*:
 * @plugindesc Full control of the action menu in battles.
 * @author Logan Pickup
 *
 * @param Enable Debugging Info
 * @desc Whether to include debugging log information and unit tests.
 * Remember to turn this OFF for release! on/off/true/false 
 * @default off
 *
 * @param Max Commands
 * @desc The maximum number of commands to display in the command list.
 * @default 4
 * 
 * @help
 * This is a combination of YEP's Weapon Unleash (which allows replacing
 * attack and guard with custom skills) and HIME's Actor Battle Command (
 * which allows customising which battle commands are shown), taken to the
 * next logical level: you can specify a custom list of actions as the
 * battle commands, including changing the order.
 * 
 * Battle commands can be specified on classes, actors, skills, armour,
 * weapons and states. Commands from classes are applied first, then actors,
 * then armour, etc. Later commands override earlier commands (especially if
 * command menu limits are enforced), and if the <battlecommand: no-defaults>
 * or <battlecommand: clear> tags are used then they remove commands that
 * may have been defined already.
 * 
 * If you don't specify visible and enabled states for the menu items, by
 * default they will use the mp/tp calculations to see if they're usable.
 * 
 * Battle commands appear in the menu in the order they are defined. This
 * means commands defined in states, etc. will mostly appear at the bottom;
 * if you want to override this order, you can specify position:x on any
 * battlecommand notetag to influence its position. Higher position values
 * make the command appear later in the list; by default the position starts
 * at 1 and goes up with every command added. You can set fractional position
 * values if you want. 
 * 
 * Changing the name of the commands isn't supported yet, but if you change
 * the name of the attack skill it will show the new attack skill's name in
 * the command list instead of "attack" (same with "guard").
 * 
 * ============================================================================
 * Symbols
 * ============================================================================
 * 
 * The battle menus are defined by "symbols". Each symbol represents a different
 * type of command, and may ahave an additional parameter: "ext", which gives
 * it more info, if needed, to carry out the command. Not all commands need the
 * "ext" parameter; for example, the default attack command has the symbol
 * "attack" and no ext; but the skill menus have the symbol "skill" and the
 * ext must be the skill type to show a menu for.
 * 
 * The default symbols are below:
 * "attack" - makes a default attack.
 * "guard" - performs a guard action.
 * "skill" - shows the skill menu for a type of skill. The skill type to show
 *           is in the "ext" parameter.
 * "item" - shows the item menu to allow an item to be selected and used.
 * 
 * Additional symbols added by this plugin are:
 * "skill_id" - performs a skill immediately, without needing to show the skill
 *              menu. The skill to perform in in the "ext" parameter.
 * "equip" - opens the weapon or armour menu to allow swapping weapon/armour.
 *           The "ext" parameter will be set to "weapon" or "armor".
 * 
 * ============================================================================
 * Notetags
 * ============================================================================
 * 
 * <battlecommand: no-defaults>
 *   Removes the default battle commands, but not any custom battle commands
 *   (even if a custom command is the same as a default command).
 * 
 * <battlecommand: clear [all|symbol] [ext:x]>
 *   Either removes all battle commands (both default and custom) defined up
 *   until this point, or removes a specific battle command by symbol and ext.
 *   If ext isn't specified, it will remove all instances of the <symbol>.
 *   
 * <battlecommand: add attack [position:x]>
 *   Adds a default attack, optionally overriding the default position.
 *   
 * <battlecommand: add guard [position:x]>
 *   Adds a default guard, optionally overriding the default position.
 *   
 * <battlecommand: add item [position:x]>
 *   Adds the default item menu, optionally overriding the default position.
 * 
 * <battlecommand: add skills [position:x]>
 *   Adds all available skill types for the actor, optionally overriding the
 *   default position. The first skill type will get the exact position value
 *   specified, and the following types will get position + 0.1, position +
 *   0.2, etc.
 *   
 * <battlecommand: add skill type [id] [position:x]>
 *   Adds a menu for skills of the specified type, optionally overriding the
 *   default position.
 *  
 * <battlecommand: add skill [id] [position:x]>
 *   Adds a command to trigger a single skill, optionally overriding the
 *   default position. If you want to rename the default attack, this is
 *   one way to do it, by just using the attack skill. Another way is
 *   using Yanfly's WEapon Unleash plugin, which adds the code to get
 *   the default attack to use the name of the skill instead of a hardcoded
 *   name. Ditto for renaming guard.
 *  
 * <battlecommand: add equip weapon [position:x]>
 *   Adds a command to switch weapons, optionally overriding the default
 *   position.
 *  
 * <battlecommand: add equip armor [position:x]>
 *   Adds a command to switch armour, optionally overriding the  default
 *   position.
 * 
 * ============================================================================
 * Advanced Notetags
 * ============================================================================
 * 
 * By putting "if" at the end of any of the <battlecommand> notetags, you can
 * use some of the built-in filtering logic to only show commands when certain
 * conditions are met. The available filters are below:
 * 
 * Left side variables:
 *   wtype: the weapon types of all the currently-weilded weapons.
 *   weapon: the weapon ids of all the currently-weilded weapons.
 *   atype: the armour types of all the currently-equipped armour.
 *   armor: the armour ids of all the currently-equipped armour.
 *   state: the state ids of all current states.
 *   vX: the variable with the id <x>.
 * 
 * Operators:
 *   ==: check passes if at least one value from the left side matches at
 *       least one value from the right side.
 *   =:  same as ==.
 *   !=: check passes if all values from the left side do not match any
 *       values from the right side.
 * 
 * Right side variables:
 *   x:  Any integer value is allowed on the right side.
 *   vX: Any variable is allowed on the right side.
 *   x,vX,x...: Lists of values and/or variables are allowed on the right
 *              side. The list may be all values, all variables, or a
 *              combination of the two. There must be no spaces between the
 *              commas and the values/variables.
 * 
 * The "if" condition can be used like this:
 * <battlecommand add skill 4 if "wtype==14">
 *  - skill 4 is added to the battle menu if the actor is using a weapon with
 *    weapon type 14.
 * 
 * Example with lists of values:
 * <battlecommand add skill 4 if "wtype==14,15,16">
 *  - skill 4 is added to the battle menu if the actor is using a weapon with
 *    weapon type of either 14, 15 or 16.
 * 
 * Example with variables:
 * <battlecommand add skill 4 if "v2==v3">
 *  - skill 4 is added to the battle menu if variable 2 is equal to variable 3.
 * 
 * Alternatively, you can specify switches:
 *   sX: The switch with id <x>. The result of the check is the value of the
 *       switch. example:
 * <battlecommand add skill 4 if "s2">
 *  - skill 4 is added to the battle menu if switch 2 is on.
 * 
 *   not sX: The opposite of the switch with id <x>. The result of the check
 *           is the negated value of the switch. example:
 * <battlecommand add skill 4 if "not s2">
 *  - skill 4 is added to the battle menu if switch 2 is off.
 * 
 * You can combine multiple conditions using "and" and "or". For example:
 * <battlecommand add skill 4 if "wtype==14 and s2">
 *  - skill 4 is added to the battle menu if the actor is using a weapon with
 *    weapon type of 14 and switch 2 is on.
 * 
 * ============================================================================
 * Custom Logic in Notetags
 * ============================================================================
 * 
 * If the built-in conditions are not enough, by putting "eval" at the end of
 * any of the <battlecommand> notetags, you can add custom logic to determine
 * if the notetag should be considered or not by setting the "show" variable,
 * for example:
 * 
 * <battlecommand add skill 4 eval>
 * if (user.weapons()[0].wtypeId === 3) {
 *   show = true;
 * }
 * </battlecommand>
 * 
 * "show" defaults to false. "eval" cannot be mixed with "if". The following
 * variables are available in adition to the standard RPG maker globals:
 * v(X) - get the value of variable number X.
 * s(X) - get the value of switch number X.
 * a - the current actor (always the actor whose battle menu we are setting).
 * user - same as a, the current actor.
 * actor - same as a.
 * skill - if the current menu item refers to a skill, this is that skill.
 */
(function() {
    function booleanParameter(param, defaultValue) {
        if (!LWP.isDefined(param) || param.trim().length === 0) {
            return defaultValue;
        } else {
            return param.toLowerCase() === 'true' || param.toLowerCase() === 'on';
        }
    }
    var parameters = new LWP.Parameters('LWP_CustomBattleMenu');
    var debugEnabled = parameters.getBoolean('Enable Debugging Info', false);
    var globalMaxBattleCommands = parameters.getInteger('Max Commands', 4);
    var logger = LWP.logger('LWP_CustomBattleMenu');
    LWP.onDatabaseLoaded(function() {
        processNotetags(this, $dataClasses);
        processNotetags(this, $dataActors);
        processNotetags(this, $dataArmors);
        processNotetags(this, $dataWeapons);
        processNotetags(this, $dataStates);
    });
    function LWP_BattleCommand(command, subCommand, ext, position, evalLogic, ifConditions) {
        this.command = command;
        this.subCommand = subCommand;
        this.ext = ext;
        this.position = position;
        this.evalLogic = evalLogic;
        this.ifConditions = ifConditions;
    }
    LWP_BattleCommand.prototype.isNoDefaults = function() {
        return this.command === 'no-defaults';
    }
    LWP_BattleCommand.prototype.isClear = function() {
        return this.command === 'clear';
    }
    LWP_BattleCommand.prototype.isAdd = function() {
        return this.command === 'add';
    }
    LWP_BattleCommand.prototype.isSkillList = function() {
        return this.isAdd() && this.subCommand === 'skills';
    }
    LWP_BattleCommand.prototype.isClearAll = function() {
        return this.isClear() && this.subCommand === 'all';
    }
    function processNotetags(dataManager, group) {
        for (var i = 1; i < group.length; i++) {
            var obj = group[i];
            var notedata = obj.note.toLowerCase();
            var re = /<battlecommand:\s*(add|clear|no-defaults)(?:\s+(\w+|all|attack|guard|item|skills|skill type|skill|equip weapon|equip armou?r)(?!\w)(?:\s+(\d+))?(?:\s+position:\s+([-0-9.]+))?)?(?: (if\s+(?:"[^"]+"|'[^']+')|eval))? *>/gi
            var match;
            obj.battleCommands = [];
            while (match = re.exec(notedata)) {
                var command = match[1] ? match[1] : match[1];
                var subCommand = match[2] ? match[2] : match[2];
                var idOrExt = match[3] ? Number(match[3]) : match[3];
                var position = match[4] ? Number(match[4]) : match[4];
                var evalLogic = null;
                if (match[5] === 'eval') {
                    var closingTagIndex = notedata.indexOf('</battlecommand>', re.lastIndex);
                    if (closingTagIndex === -1) {
                        logger.error("Error: no closing tag found for " + match[0] + " in " + obj.name);
                    } else {
                        evalLogic = notedata.substring(re.lastIndex, closingTagIndex).trim();
                        re.lastIndex = closingTagIndex + '</battlecommand>'.length;
                    }
                }
                var ifConditions = null;
                if (match[5] && match[5].startsWith('if')) {
                    var expression = match[5].substring(2, match[5].length - 1);
                    expression = expression.trim().substring(1);
                    ifConditions = parseIfConditions(expression);
                }
                var battleCommand = new LWP_BattleCommand(command, subCommand, idOrExt, position, evalLogic, ifConditions);
                obj.battleCommands.push(battleCommand);
            }
        };
    }
    Scene_Battle.prototype.createActorCommandWindow = Scene_Battle.prototype.createActorCommandWindow.wrap(function(createActorCommandWindow) {
        createActorCommandWindow();
        this._actorCommandWindow.setHandler('skill_id', this.commandUseSkill.bind(this));
        this._actorCommandWindow.setHandler('equip', this.commandEquip.bind(this));
    });
    Scene_Battle.prototype.commandUseSkill = function() {
        var skillId = this._actorCommandWindow.currentExt();
        var action = BattleManager.inputtingAction();
        action.setSkill(skillId);
        BattleManager.actor().setLastBattleSkill($dataSkills[skillId]);
        this.onSelectAction();
    }
    Scene_Battle.prototype.commandEquip = function() {
        if (this._actorCommandWindow.currentExt() === 'weapon') {
            // equip weapon
            throw "TODO";
        } else if (this._actorCommandWindow.currentExt() === 'armor') {
            // equip armour
            throw "TODO";
        } else {
            logger.error("Unknown equip type " + this._actorCommandWindow.currentExt());
        }
    }
    function getBattleCommandsFromObject(dataObject) {
        return dataObject.battleCommands || [];
    }
    function getBattleCommandsFromArray(dataObjects) {
        var battleCommands = [];
        for (obj in dataObjects) {
            battleCommands = battleCommands.concat(getBattleCommandsFromObject(obj));
        }
        return battleCommands;
    }
    function findCommands(list, symbol, ext) {
        var indexes = [];
        for (var i = 0; i < list.length; ++i) {
            if (list[i].symbol === symbol) {
                if (!LWP.isDefined(ext) || ext === list[i].ext) {
                    indexes.push(i);
                }
            }
        }
        return indexes;
    }
    function findCommand(list, symbol, ext) {
        for (var i = 0; i < list.length; ++i) {
            if (list[i].symbol === symbol) {
                if (!LWP.isDefined(ext) || ext === list[i].ext) {
                    return i;
                }
            }
        }
        return -1;
    }
    function getBattleCommandsForActor(actor) {
        var battleCommands = [];
        battleCommands = battleCommands.concat(getBattleCommandsFromObject(actor.currentClass()));
        battleCommands = battleCommands.concat(getBattleCommandsFromObject(actor.actor()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.skills()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.armors()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.weapons()));
        battleCommands = battleCommands.concat(getBattleCommandsFromArray(actor.states()));
        return battleCommands;
    }
    function LWP_CommandListItem(symbol, ext, position, isDefault) {
        this.symbol = symbol;
        this.ext = ext;
        this.position = position;
        this.isDefault = isDefault;
    }
    function replaceOrAdd(commandList, command, forcePosition) {
        if (!command) {
            return;
        }
        var existing = findCommand(commandList, command.symbol, command.ext);
        if (existing != -1) {
            if (forcePosition) {
                commandList[existing].position = command.position;
            }
            commandList[existing].isDefault = commandList[existing].isDefault && command.isDefault;
        } else {
            commandList.push(command);
        }
    }
    function addSkillTypes(commandList, actor, position, isDefault) {
        var currentPosition = LWP.isDefined(position) ? position : commandList.length;
        var skillTypes = actor.addedSkillTypes();
        skillTypes.sort(function(a, b) {
            return a - b;
        });
        skillTypes.forEach(function(sTypeId) {
            var command = new LWP_CommandListItem('skill', sTypeId, currentPosition, isDefault);
            replaceOrAdd(commandList, command, LWP.isDefined(position));
            currentPosition += 0.1;
        }, this);
        return commandList;
    }
    function translateBattleCommand(battleCommand, actor, defaultPosition, isDefault) {
        var ext = battleCommand.ext;
        var position = LWP.isDefined(battleCommand.position) ? battleCommand.position : defaultPosition;
        switch (battleCommand.subCommand) {
            case 'attack':
                return new LWP_CommandListItem('attack', null, position, isDefault);
            case 'guard':
                return new LWP_CommandListItem('guard', null, position, isDefault);
            case 'item':
                return new LWP_CommandListItem('item', null, position, isDefault);
            case 'skill type':
                if (actor.addedSkillTypes().indexOf(ext) !== -1) {
                    return new LWP_CommandListItem('skill', ext, position, isDefault);
                } else {
                    logger.error("Error: attempt to add skill type " + ext + " to actor " + actor.name() + " who doesn't have that skill type");
                    logger.debug(actor);
                    return null;
                }
            case 'skill':
                if (actor.skills().map(function(x) {return x.id}).indexOf(ext) !== -1) {
                    return new LWP_CommandListItem('skill_id', ext, position, isDefault);
                } else {
                    // not necessarily an error, as this can be a way to insert specific skills high
                    // in the list if an actor happens to know them
                    logger.warning("Warning: attempt to add skill " + ext + " to actor " + actor.name() + " who doesn't know that skill");
                    logger.debug(actor.skills());
                    return null;
                }
            case 'equip weapon':
                return new LWP_CommandListItem('equip', 'weapon', position, isDefault);
            case 'equip armor':
            case 'equip armour':
                return new LWP_CommandListItem('equip', 'armor', position, isDefault);
            default:
                logger.error("Error: unhandled command " + battleCommand.command + ":" + battleCommand.subCommand);
                return null;
        }
    }
    function applyBattleCommand(commandList, battleCommand, actor) {
        var position = LWP.isDefined(battleCommand.position) ? battleCommand.position : commandList.length;
        if (battleCommand.isNoDefaults()) {
            return commandList.filter(function(command) {!command.isDefault;});
        } else if (battleCommand.isClearAll()) {
            return [];
        } else if (battleCommand.isClear()) {
            var found = findCommands(commandList, battleCommand.subCommand, battleCommand.ext);
            // process the indexes in reverse, otherwise the later indexes will change as the list
            // is altered
            found.reverse().forEach(function(index) {
                commandList.splice(index, 1);
            });
            return commandList;
        } else if (battleCommand.isAdd()) {
            if (battleCommand.isSkillList()) {
                return addSkillTypes(commandList, actor, battleCommand.position, false);
            } else {
                var newCommand = translateBattleCommand(battleCommand, actor, position, false);
                replaceOrAdd(commandList, newCommand, LWP.isDefined(battleCommand.position));
                return commandList;
            }
        } else {
            logger.error("Error: unhandled command " + battleCommand.command + ":" + battleCommand.subCommand);
            return commandList;
        }
    }
    function addDefaultCommands(commandList, actor) {
        commandList.push(new LWP_CommandListItem('attack', null, commandList.length, true));
        commandList = addSkillTypes(commandList, actor, null, true);
        commandList.push(new LWP_CommandListItem('guard', null, commandList.length, true));
        commandList.push(new LWP_CommandListItem('item', null, commandList.length, true));
        return commandList;
    }
    function filteredBattleCommands(battleCommands, actor) {
        return battleCommands.filter(function(command) {
            if (command.evalLogic) {
                // globals meant to be commonly available to evals
                var v = function(x) {return $gameVariables.value(x);};
                var s = function(x) {return $gameSwitches.value(x);};
                var a = actor;
                var user = actor;
                var show = false;
                var skill = actor.skills().filter(function(x) {return x.id === command.ext});
                var skill = skill.length > 0 ? skill[0] : null;
                eval(command.evalLogic);
                return show;
            } else if (command.ifConditions) {
                return command.ifConditions(actor);
            } else {
                return true;
            }
        });
    }
    Window_ActorCommand.prototype.maxBattleCommands = function() {
        return globalMaxBattleCommands;
    }
    Window_ActorCommand.prototype.makeCommandList = Window_ActorCommand.prototype.makeCommandList.wrap(function(makeCommandList) {
        if (this._actor) {
            var actor = this._actor;
            // get the raw command definitions
            var battleCommands = getBattleCommandsForActor(actor);
            battleCommands = filteredBattleCommands(battleCommands, actor);
            if (battleCommands.length == 0) {
                // early out if there are no battle commands defined
                makeCommandList();
                return;
            }
            logger.debug("Battle commands:");
            logger.debug(battleCommands);
            var commandList = [];
            commandList = addDefaultCommands(commandList, actor);
            logger.debug("Command list - default commands:");
            logger.debug(commandList);
            // put them in an intermediate format, and apply clears/overrides
            battleCommands.forEach(function(command) {
                commandList = applyBattleCommand(commandList, command, actor);
            });
            logger.debug("Command list - after adding commands:");
            logger.debug(commandList);
            // sort according to position
            commandList.sort(function(a, b) {
                return a.position - b.position;
            });
            logger.debug("Command list - after sorting:");
            logger.debug(commandList);
            // limit command list length
            commandList = commandList.slice(0, this.maxBattleCommands());
            logger.debug("Command list - after truncating to maximum length:");
            logger.debug(commandList);
            // add the commands to the menu
            var self = this;
            commandList.forEach(function(command) {
                if (command.symbol === 'attack') {
                    self.addAttackCommand();
                } else if (command.symbol === 'guard') {
                    self.addGuardCommand();
                } else if (command.symbol === 'item') {
                    self.addItemCommand();
                } else if (command.symbol === 'skill') {
                    var name = $dataSystem.skillTypes[command.ext];
                    self.addCommand(name, command.symbol, true, command.ext);
                } else if (command.symbol === 'skill_id') {
                    var skill = $dataSkills[command.ext];
                    var name = skill.commandAttackText || skill.name;
                    var enabled = actor.meetsSkillConditions(skill);
                    self.addCommand(name, command.symbol, enabled, command.ext);
                } else if (command.symbol === 'equip') {
                    var enabled = false;
                    self.addCommand(command.ext, command.symbol, enabled, command.ext);
                }
            });
        } else {
            makeCommandList();
        }
    });
    //////////////////////////////////////////////////////////////////
    // Expression Parser
    //
    // Note that this hasn't been well tested; only a few combinations
    // are known for certain to work. It's proably OK though! :)
    //////////////////////////////////////////////////////////////////
    // parse tree:
    // or-expression-part: and-expresion | simple-expression
    // or-expression: or-expression-part "or" or-expression-part [ "or" or-expression-part ...]
    // and-expression-part: simple-expression
    // and-expression: and-expression-part "and" and-expression-part [ "and" and-expression-part ...]
    // simple-expression: variable-expression | switch-expression
    // switch-expression: [not] s + integer
    // variable-expression: variable operator value-list
    // variable: actor-prop | variable-id
    // operator: "==" | "=" | "!="
    // value-list: value[,value...] (note: no spaces allowed in this list because reasons (i.e. I'm lazy))
    // value: integer | variable-id
    // variable-id: "v" + integer
    function parseIfConditions(expression) {
        return parseOr(expression, expression);
    }
    function parseOr(subexpression, expression) {
        var orClauses = subexpression.split(/\s+or\s+/);
        return combineOrClauses(orClauses.map(function(orClause) {
            return parseAnd(orClause, expression);
        }));
    }
    function parseAnd(subexpression, expression) {
        var andClauses = subexpression.split(/\s+and\s+/);
        return combineAndClauses(andClauses.map(function(andClause) {
            return parseSimpleExpression(andClause, expression);
        }));
    }
    function combineOrClauses(booleanFunctions) {
        return function(actor) {
            var result = false;
            logger.debug("OR (" + booleanFunctions.length + ")");
            booleanFunctions.forEach(function(fn) {
                result = result || fn(actor);
                logger.debug(result);
            });
            return result;
        }
    }
    function combineAndClauses(booleanFunctions) {
        return function(actor) {
            var result = true;
            logger.debug("AND (" + booleanFunctions.length + ")");
            booleanFunctions.forEach(function(fn) {
                result = result && fn(actor);
                logger.debug(result);
            });
            return result;
        }
    }
    
    var variableAccessors = {
        'wtype': function(actor) {
            return actor.weapons().map(function(x) {return x.wtypeId;});
        },
        'weapon': function(actor) {
            return actor.weapons().map(function(x) {return x.baseItemId;});
        },
        'armor': function(actor) {
            return actor.armors().map(function(x) {return x.baseItemId;});
        },
        'atype': function(actor) {
            return actor.armors().map(function(x) {return x.atypeId;});
        },
        'state': function(actor) {
            return actor.states().map(function(x) {return x.id;});
        },
    };
    function variableAccessor(variableExpression) {
        var variableId = +variableExpression.substring(1);
        return function() {return $gameVariables.value(variableId);};
    }
    function parseSimpleExpression(expression, context) {
        var re = /\s*([a-z0-9]+)\s*(==?|!=)\s*([v0-9,]+)\s*|\s*(?:(not) )?s([0-9]+)\s*/i;
        var parts = re.exec(expression);
        if (parts && parts[1]) {
            var variable = parts[1];
            var operator = parts[2];
            var valueGenerators = parts[3].split(",").map(function(x) {
                if (x.startsWith('v')) {
                    return variableAccessor(x);
                } else {
                    return function() {return +x;};
                }
            });
            var accessor = variableAccessors[variable];
            if (!accessor && /v[0-9]+/.test(variable)) {
                accessor = variableAccessor(variable);
            }
            if (!accessor) {
                logger.error("Error: no accessor found for variable " + variable + " in expression " + expression + " in: " + context);
                return function() {return false;};
            }
            return function(actor) {
                logger.debug("actor:", actor);
                var lValues = accessor(actor);
                if (!LWP.isDefined(lValues.length)) {
                    lValues = [lValues];
                }
                var values = valueGenerators.map(function(x) {return x();});
                logger.debug(expression);
                logger.debug(lValues, values);
                if (operator === '==' || operator === '=') {
                    return lValues
                        .map(function(l) { return values.indexOf(l) !== -1; })
                        .reduce(function(previous, current) {return previous || current}, false);
                } else if (operator === '!=') {
                    return lValues
                        .map(function(l) { return values.indexOf(l) === -1; })
                        .reduce(function(previous, current) {return previous && current}, true);
                } else {
                    logger.error("Error: unhandled operator " + operator + " in " + expression + " in: " + context);
                    return false;
                }
            };
        } else if (parts && parts[5]) {
            var not = !!parts[4];
            var switchId = +parts[5];
            if (not) {
                return function() {
                    logger.debug("not switch", $gameSwitches.value(switchId));
                    return !$gameSwitches.value(switchId);
                };
            } else {
                return function() {
                    logger.debug("switch", $gameSwitches.value(switchId));
                    return $gameSwitches.value(switchId);
                };
            }
        } else {
            logger.error("Error: malformed expression " + expression + " in: " + context);
            return function() {return false;};
        }
    }
    ////////////////// TESTS ///////////////////
    DataManager.createGameObjects = DataManager.createGameObjects.wrap(function(createGameObjects) {
        createGameObjects();
        if (debugEnabled) {
            logger.setLogLevel(LWP.LOG_VERBOSE);
            expressionParserTests();
        }
    });
    function expressionParserTests() {
        logger.info("Tests starting");
        testActorPropWeaponType();
        testActorPropWeaponId();
        testActorPropArmorType();
        testActorPropArmorId();
        testActorPropState();
        testActorPropEqualsExpressionTrue();
        testActorPropEqualsExpressionFalse();
        testActorPropNotEqualsExpressionTrue();
        testActorPropNotEqualsExpressionFalse();
        testActorPropEqualsVariableTrue();
        testActorPropEqualsVariableFalse();
        testActorPropNotEqualsVariableTrue();
        testActorPropNotEqualsVariableFalse();
        testActorPropEqualsListTrue();
        testActorPropEqualsListFalse();
        testActorPropNotEqualsListTrue();
        testActorPropNotEqualsListFalse();
        testActorMultiPropSomeInListEqualsTrue();
        testActorMultiPropAllInListEqualsTrue();
        testActorMultiPropAllNotInListEqualsFalse();
        testActorMultiPropAllNotInListNotEqualsTrue();
        testActorMultiPropSomeInListNotEqualsFalse();
        testActorMultiPropAllInListNotEqualsFalse();
        testActorMultiPropSomeInListWithVariablesEqualsTrue();
        testVariableEqualsValue();
        testVariableEqualsVariable();
        testVariableEqualsList();
        testSwitchOn();
        testSwitchOff();
        testSwitchNotOn();
        testSwitchNotOff();
        testEqualsTrueAndEqualsTrue();
        testEqualsTrueAndEqualsFalse();
        testEqualsFalseAndEqualsTrue();
        testEqualsFalseAndEqualsFalse();
        testEqualsTrueAndSwitchTrue();
        testEqualsTrueAndNotSwitchTrue();
        testEqualsTrueOrEqualsTrue();
        testEqualsTrueOrEqualsFalse();
        testEqualsFalseOrEqualsTrue();
        testEqualsFalseOrEqualsFalse();
        testEqualsFalseOrSwitchTrue();
        testEqualsTrueOrNotSwitchFalse();
        testAndPrecedenceHigherThanOr();
        testLongAndOrChain();
        logger.info("Tests finished");
    }
    function testActorPropWeaponType() {
        givenTheExpression("wtype==1")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropWeaponId() {
        givenTheExpression("weapon==3")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropArmorType() {
        givenTheExpression("atype==1")
            .andAnActorWith().armor({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropArmorId() {
        givenTheExpression("armor==3")
            .andAnActorWith().armor({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropState() {
        givenTheExpression("state==3")
            .andAnActorWith().state(3)
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsExpressionTrue() {
        givenTheExpression("wtype==1")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsExpressionFalse() {
        givenTheExpression("wtype==1")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropNotEqualsExpressionTrue() {
        givenTheExpression("wtype!=1")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropNotEqualsExpressionFalse() {
        givenTheExpression("wtype!=1")
            .andAnActorWith().weapon({id: 3, type: 1})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropEqualsVariableTrue() {
        givenTheExpression("wtype==v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsVariableFalse() {
        givenTheExpression("wtype==v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropNotEqualsVariableTrue() {
        givenTheExpression("wtype!=v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropNotEqualsVariableFalse() {
        givenTheExpression("wtype!=v3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .andAVariable({id: 3, value: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropEqualsListTrue() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropEqualsListFalse() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith().weapon({id: 3, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorPropNotEqualsListTrue() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith().weapon({id: 3, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorPropNotEqualsListFalse() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith().weapon({id: 3, type: 2})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropSomeInListEqualsTrue() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorMultiPropAllInListEqualsTrue() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 2})
                .weapon({id: 3, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorMultiPropAllNotInListEqualsFalse() {
        givenTheExpression("wtype==1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 5})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropAllNotInListNotEqualsTrue() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 5})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testActorMultiPropSomeInListNotEqualsFalse() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 2})
                .weapon({id: 3, type: 5})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropAllInListNotEqualsFalse() {
        givenTheExpression("wtype!=1,2,3")
            .andAnActorWith()
                .weapon({id: 3, type: 2})
                .weapon({id: 3, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testActorMultiPropSomeInListWithVariablesEqualsTrue() {
        givenTheExpression("wtype==v1,v2,v3")
            .andAnActorWith()
                .weapon({id: 3, type: 4})
                .weapon({id: 3, type: 6})
            .andAVariable({id: 1, value: 5})
            .andAVariable({id: 2, value: 5})
            .andAVariable({id: 3, value: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    // At this point, we've fairly exhaustively testing the equals and not equals
    // operators. We'll skip every single variation, and focus on different
    // combinations of left- and right-hand values.
    function testVariableEqualsValue() {
        givenTheExpression("v1==3")
            .andAVariable({id: 1, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testVariableEqualsVariable() {
        givenTheExpression("v1==v2")
            .andAVariable({id: 1, value: 3})
            .andAVariable({id: 2, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testVariableEqualsList() {
        givenTheExpression("v1==2,3,4")
            .andAVariable({id: 1, value: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testSwitchOn() {
        givenTheExpression("s3")
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testSwitchOff() {
        givenTheExpression("s3")
            .andASwitch({id: 3, value: false})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testSwitchNotOn() {
        givenTheExpression("not s3")
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testSwitchNotOff() {
        givenTheExpression("not s3")
            .andASwitch({id: 3, value: false})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    // and/or
    function testEqualsTrueAndEqualsTrue() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueAndEqualsFalse() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsFalseAndEqualsTrue() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsFalseAndEqualsFalse() {
        givenTheExpression("wtype==4 and weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsTrueAndSwitchTrue() {
        givenTheExpression("wtype==4 and s3")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueAndNotSwitchTrue() {
        givenTheExpression("wtype==4 and not s3")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .andASwitch({id: 3, value: false})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueOrEqualsTrue() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueOrEqualsFalse() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 4})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsFalseOrEqualsTrue() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 2, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsFalseOrEqualsFalse() {
        givenTheExpression("wtype==4 or weapon==2")
            .andAnActorWith()
                .weapon({id: 1, type: 3})
            .whenTheConditionIsChecked()
            .theConditionShouldFail();
    }
    function testEqualsFalseOrSwitchTrue() {
        givenTheExpression("wtype==4 or s3")
            .andAnActorWith()
                .weapon({id: 2, type: 3})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testEqualsTrueOrNotSwitchFalse() {
        givenTheExpression("wtype==4 or not s3")
            .andAnActorWith()
                .weapon({id: 2, type: 4})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testAndPrecedenceHigherThanOr() {
        givenTheExpression("s3 or v1==2 and v2==3")
            .andAVariable({id: 1, value: 2})
            .andAVariable({id: 2, value: 2})
            .andASwitch({id: 3, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    function testLongAndOrChain() {
        givenTheExpression("not s1 or not s2 or s3 and s4 and s5 or not s6 and s7 or not s8")
            .andASwitch({id: 1, value: true})
            .andASwitch({id: 2, value: true})
            .andASwitch({id: 3, value: true})
            .andASwitch({id: 4, value: true})
            .andASwitch({id: 5, value: true})
            .andASwitch({id: 6, value: true})
            .andASwitch({id: 7, value: true})
            .andASwitch({id: 8, value: true})
            .whenTheConditionIsChecked()
            .theConditionShouldPass();
    }
    // Test framework
    function givenTheExpression(expression) {
        var TARGET_ACTOR = 'the actor';
        return {
            tag: "Given an expression \"" + expression + "\"\n",
            currentlyAnnotating: null,
            tagNeedsNewline: false,
            variables: {},
            switches: {},
            actorProps: {
                weapons: [],
                armors: [],
                states: []
            },
            condition: parseIfConditions(expression),
            andAnActorWith: function() {
                this._fixAnnotations(null);
                this.tag += "And an actor with ";
                this.currentlyAnnotating = TARGET_ACTOR;
                this.tagNeedsNewline = true;
                return this;
            },
            weapon: function(data) {
                this._fixAnnotations(TARGET_ACTOR);
                this.tag += "a weapon (id=" + data.id + ", type=" + data.type + ") ";
                this.actorProps.weapons.push(makeWeapon(data.id, data.type));
                return this;
            },
            armor: function(data) {
                this._fixAnnotations(TARGET_ACTOR);
                this.tag += "armour (id=" + data.id + ", type=" + data.type + ") ";
                this.actorProps.armors.push(makeArmor(data.id, data.type));
                return this;
            },
            state: function(id) {
                this._fixAnnotations(TARGET_ACTOR);
                this.tag += "state " + id + " ";
                this.actorProps.states.push({id: id});
                return this;
            },
            andAVariable: function(data) {
                this._fixAnnotations(null);
                this.tag += "And a variable " + data.id + " with the value " + data.value + "\n";
                this.variables[data.id] = data.value;
                return this;
            },
            andASwitch: function(data) {
                this._fixAnnotations(null);
                this.tag += "And a switch " + data.id + " that is " + (data.value ? "on" : "off") + "\n";
                this.switches[data.id] = data.value;
                return this;
            },
            whenTheConditionIsChecked: function() {
                this._fixAnnotations(null);
                this.tag += "When the condition is checked\n";
                this.actor = dummyActor(this.actorProps.weapons, this.actorProps.armors, this.actorProps.states);
                this._setVariables();
                this.checkResult = this.condition(this.actor);
                this._restoreVariables();
                return this;
            },
            theConditionShouldPass: function() {
                this._fixAnnotations(null);
                this.tag += "Then the condition should pass\n";
                this._assert(this.checkResult, this.tag);
                logger.info(this.tag + " - PASSED");
                return this;
            },
            theConditionShouldFail: function() {
                this._fixAnnotations(null);
                this.tag += "Then the condition should fail\n";
                this._assert(!this.checkResult, this.tag);
                logger.info(this.tag + " - PASSED");
                return this;
            },
            _setVariables: function() {
                this.oldVariableValues = {}
                for (var variableId in this.variables) {
                    if (this.variables.hasOwnProperty(variableId)) {
                        this.oldVariableValues = $gameVariables.value(variableId);
                        $gameVariables.setValue(variableId, this.variables[variableId]);
                    }
                }
                this.oldSwitchValues = {}
                for (var switchID in this.switches) {
                    if (this.switches.hasOwnProperty(switchID)) {
                        this.oldSwitchValues = $gameSwitches.value(switchID);
                        $gameSwitches.setValue(switchID, this.switches[switchID]);
                    }
                }
            },
            _restoreVariables: function() {
                for (var variableId in this.variables) {
                    if (this.variables.hasOwnProperty(variableId)) {
                        $gameVariables.setValue(variableId, this.oldVariableValues[variableId]);
                    }
                }
                for (var switchID in this.switches) {
                    if (this.switches.hasOwnProperty(switchID)) {
                        $gameSwitches.setValue(switchID, this.oldSwitchValues[switchID]);
                    }
                }
            },
            _fixAnnotations: function(forAnnotationTarget) {
                if (this.currentlyAnnotating != forAnnotationTarget) {
                    if (this.tagNeedsNewline) {
                        this.tag += '\n';
                        this.tagNeedsNewline = false;
                    }
                    this.currentlyAnnotating = forAnnotationTarget;
                    if (forAnnotationTarget) {
                        this.tag += "And " + forAnnotationTarget + " has ";
                        this.tagNeedsNewline = true;
                    }
                }
            },
            _assert: function(test, message) {
                if (!test) {
                    throw new Error(message);
                }
            }
        };
    }
    function dummyActor(weapons, armors, states) {
        if (!weapons) weapons = [];
        if (!LWP.isDefined(weapons.length)) weapons = [weapons];
        if (!armors) armors = [];
        if (!LWP.isDefined(armors.length)) armors = [armors];
        if (!states) states = [];
        if (!LWP.isDefined(states.length)) states = [states];
        return {
            weapons: function() {return weapons;},
            armors: function() {return armors;},
            states: function() {return states;}
        }
    }
    function makeWeapon(id, type) {
        return {
            baseItemId: id,
            wtypeId: type
        }
    }
    function makeArmor(id, type) {
        return {
            baseItemId: id,
            atypeId: type
        }
    }
})();
 

ShiningRomantica

Villager
Member
Joined
Mar 9, 2020
Messages
18
Reaction score
1
First Language
Portuguese
Primarily Uses
RMMV
It's working perfectly now. Thank you! :3
 

theartofme

Villager
Member
Joined
Feb 21, 2019
Messages
21
Reaction score
31
First Language
English
Primarily Uses
RMMV
No problem! Glad it was useful :)
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Profile Posts

Getting work done in the hospital is a little tough :kaosigh:
Yet another custom skill animation, sometimes i waste too much time on details people will barely notice :kaocry:
Time flies by... already 1/6th of the calendar released :o

Forum statistics

Threads
105,823
Messages
1,016,723
Members
137,521
Latest member
HunterDev
Top