//=============================================================================
// QMovement
//=============================================================================

var Imported = Imported || {};

if (!Imported.QPlus || !QPlus.versionCheck(Imported.QPlus, '1.6.0')) {
  alert('Error: QMovement requires QPlus 1.6.0 or newer to work.');
  throw new Error('Error: QMovement requires QPlus 1.6.0 or newer to work.');
}

Imported.QMovement = '1.6.3';

//=============================================================================
/*:
 * @plugindesc <QMovement>
 * More control over character movement
 * @version 1.6.3
 * @author Quxios  | Version 1.6.3
 * @site https://quxios.github.io/
 * @updateurl https://quxios.github.io/data/pluginsMin.json
 *
 * @repo https://github.com/quxios/QMovement
 *
 * @requires QPlus
 *
 * @video TODO
 *
 * @param Main Settings
 *
 * @param Grid
 * @parent Main Settings
 * @desc The amount of pixels you want to move per Movement.
 * Plugin Default: 1   MV Default: 48
 * @type Number
 * @min 1
 * @default 1
 *
 * @param Tile Size
 * @parent Main Settings
 * @desc Size of tiles in pixels
 * Default: 48
 * @type Number
 * @min 1
 * @default 48
 *
 * @param Off Grid
 * @parent Main Settings
 * @desc Allow characters to move off grid?
 * @type boolean
 * @on Yes
 * @off No
 * @default true
 *
 * @param Optional Settings
 *
 * @param Smart Move
 * @parent Optional Settings
 * @desc If the move didn't succeed, try again at lower speeds or at
 * a different direction
 * @type select
 * @option Disabled
 * @value 0
 * @option Adjust Speed
 * @value 1
 * @option Adjust Direction
 * @value 2
 * @option Adjust Speed and Direction
 * @value 3
 * @default 2
 *
 * @param Mid Pass
 * @parent Optional Settings
 * @desc An extra collision check for the midpoint of the Movement.
 * @type boolean
 * @on Enable
 * @off Disable
 * @default false
 *
 * @param Move on click
 * @parent Optional Settings
 * @desc Set if player moves with mouse click
 * * Requires QPathfind to work
 * @type boolean
 * @on Enable
 * @off Disable
 * @default true
 *
 * @param Diagonal
 * @parent Optional Settings
 * @desc Allow for diagonal movement?
 * @type boolean
 * @on Yes
 * @off No
 * @default true
 *
 * @param Diagonal Speed
 * @parent Optional Settings
 * @desc Adjust the speed when moving diagonal.
 * Default: 0 TODO not functional
 * @type Number
 * @min 0
 * @default 0
 *
 * @param Colliders
 *
 * @param Player Collider
 * @text Default Player Collider
 * @parent Colliders
 * @desc Default collider for the player.
 * @type Struct<Collider>
 * @default {"Type":"box","Width":"36","Height":"24","Offset X":"6","Offset Y":"24"}
 *
 * @param Event Collider
 * @text Default Event Collider
 * @parent Colliders
 * @desc Default collider for events.
 * @type Struct<Collider>
 * @default {"Type":"box","Width":"36","Height":"24","Offset X":"6","Offset Y":"24"}
 *
 * @param Presets
 * @parent Colliders
 * @desc List of preset colliders that you can assign to
 * events
 * @type Struct<ColliderPreset>[]
 * @default []
 *
 * @param Debug Settings
 *
 * @param Show Colliders
 * @parent Debug Settings
 * @desc Show the Box Colliders by default during testing.
 * -Can be toggled on/off with F10 during play test
 * @type boolean
 * @on Show by default
 * @off Hide by default
 * @default true
 *
 * @help
 * ============================================================================
 * ## About
 * ============================================================================
 * This plugin completely rewrites the collision system to use colliders. Using
 * colliders enabled more accurate collision checking with dealing with pixel
 * movement. This plugin also lets you change how many pixels the characters
 * move per step, letting you set up a 24x24 movement or a 1x1 (pixel movement)
 *
 * Note there are a few mv features disabled/broken; mouse movement, followers,
 * and vehicles.
 * ============================================================================
 * ## Setting up
 * ============================================================================
 * To setup a pixel based movement, you'll need to change the plugin parameters
 * to something like:
 *
 * - Grid = 1
 * - Off Grid = true
 * - Mid Pass = false
 *
 * Other parameters can be set to your preference.
 *
 * For a grid based movement, set it something like:
 *
 * - Grid = GRIDSIZE YOU WANT
 * - Off Grid = false
 * - Mid Pass = true
 *
 * When in grid based movement, you want your colliders to fill up most of the
 * grid size but with a padding of 4 pixels on all sides (this is because some
 * tile colliders are 4 tiles wide or tall). So if your grid size was 48, your
 * colliders shouldn't be 48x48, instead they should be 40x40, with an ox and oy
 * of 4. So your collider setting would look like: box, 40, 40, 4, 4
 * ============================================================================
 * ## Colliders
 * ============================================================================
 * There are 3 types of colliders; Polygon, Box and Circle. Though you can only
 * create box and circle colliders, unless you modify the code to accept
 * polygons. This is intentional since polygon would be "harder" to setup.
 *
 * ![Colliders Image](https://quxios.github.io/imgs/qmovement/colliders.png)
 *
 * - Boxes takes in width, height, offset x and offset y
 * - Circles similar to boxes, takes in width, height, offset x and offset y
 * ----------------------------------------------------------------------------
 * **Setting up colliders**
 * ----------------------------------------------------------------------------
 * Colliders are setup inside the Players notebox or as a comment inside an
 * Events page. Events colliders depends it's page, so you may need to make the
 * collider on all pages.
 *
 * There are two ways to setup colliders. using `<collider:-,-,-,->` and using
 * `<colliders>-</colliders>`. The first method sets the 'Default' collider for
 * that character. The second one you create the colliders for every collider
 * type.
 * ----------------------------------------------------------------------------
 * **Collider Types**
 * ----------------------------------------------------------------------------
 * There are 3 collider types. Default, Collision and Interaction.
 * - Default: This is the collider to use if collider type that was called was
 * not found
 * - Collision: This collider is used for collision checking
 * - Interaction: This collider is used for checking interaction.
 * ----------------------------------------------------------------------------
 * **Collider Presets**
 * ----------------------------------------------------------------------------
 * You can create colliders in the plugin parameters which you can use when
 * setting up other colliders.
 * ----------------------------------------------------------------------------
 * **Collider Terms**
 * ----------------------------------------------------------------------------
 * ![Colliders Terms Image](https://quxios.github.io/imgs/qmovement/colliderInfo.png)
 * ----------------------------------------------------------------------------
 * **Collider Notetag**
 * ----------------------------------------------------------------------------
 * ~~~
 *  <collider: [SHAPE], [WIDTH], [HEIGHT], [OX], [OY]>
 * ~~~
 * This notetag sets all collider types to these values.
 * - SHAPE: Set to box, circle or poly
 *   - If poly read next section on poly shape
 * - WIDTH: The width of the collider, in pixels
 * - HEIGHT: The height of the collider, in pixels
 * - OX: The X Offset of the collider, in pixels
 * - OY: The Y Offset of the collider, in pixels
 * ----------------------------------------------------------------------------
 * **Colliders Notetag**
 * ----------------------------------------------------------------------------
 * ~~~
 *  <colliders>
 *  [TYPE]: [SHAPE], [WIDTH], [HEIGHT], [OX], [OY]
 *  </colliders>
 * ~~~
 * This notetag sets all collider types to these values.
 * - TYPE: The type of collider, set to default, collision or interaction
 * - SHAPE: Set to box, circle or poly
 *   - If poly read next section on poly shape
 * - WIDTH: The width of the collider, in pixels
 * - HEIGHT: The height of the collider, in pixels
 * - OX: The X Offset of the collider, in pixels
 * - OY: The Y Offset of the collider, in pixels
 *
 * To add another type, just add `type: shape, width, height, ox, oy` on
 * another line.
 *
 * Example:
 * ~~~
 *  <colliders>
 *  default: box, 48, 48
 *  collision: circle, 24, 24, 12, 12
 *  interaction: box, 32, 32, 8, 8
 *  </colliders>
 * ~~~
 * ----------------------------------------------------------------------------
 * **Using Preset**
 * ----------------------------------------------------------------------------
 * To use a collider preset in the notetag, the format is:
 * ~~~
 *  preset, [PRESETID]
 * ~~~
 * - PRESETID: The PresetID you set in the preset parameter.
 *
 * You will use this format instead of the: `SHAPE, WIDTH, HEIGHT, OX, OY`
 *
 * Example:
 * ~~~
 *  <collider: preset, largeCollider>
 * ~~~
 * Will look for the preset with the ID `largeCollider`
 *
 * Example 2:
 * ~~~
 *  <colliders>
 *  default: preset, largeDefault
 *  collision: preset, largeCollider
 *  interaction: preset, largeInteraction
 *  </colliders>
 * ~~~
 * Will use the presets; `largeDefault`, `largeCollider`, and `largeInteraction`
 * ----------------------------------------------------------------------------
 * **Poly Colliders**
 * ----------------------------------------------------------------------------
 * To create a polygon collider, set the shape to poly. After that the rest
 * of the line should be a list of points separated with a comma. Points are
 * written as "(X,Y)". An example polygon would be:
 * ~~~
 *  poly,(24,0),(48,24),(24,48),(0,24)
 * ~~~
 * Would create a diamond shaped polygon.
 *
 * Example of using it inside a collider tag
 * ~~~
 *  <collider:poly,(24,0),(48,24),(24,48),(0,24)>
 * ~~~
 * ============================================================================
 * ## Move Routes
 * ============================================================================
 * By default, event move commands (moveup, movedown, ect) will convert to a
 * qmove that moves the character based off your tilesize. So if your tilesize
 * is 48 and your gridsize is 1. Then a moveup command will move the character
 * up 48 pixels not 1. But if you want to move the character by a fixed amount
 * of pixels, then you will use the QMove commands.
 * ----------------------------------------------------------------------------
 * **QMove**
 * ----------------------------------------------------------------------------
 * ![QMove Script Call](https://quxios.github.io/imgs/qmovement/qmove.png)
 *
 * To do a QMove, add a script in the move route in the format:
 * ~~~
 *  qmove(DIR, AMOUNT, MULTIPLER)
 * ~~~
 * - DIR: Set to a number representing the direction to move;
 *  - 4: left, 6: right, 8: up 2: down,
 *  - 1: lower left, 3: lower right, 7: upper left, 9: upper right,
 *  - 5: current direction, 0: reverse direction
 * - AMOUNT: The amount to move in that direction, in pixels
 * - MULTIPLIER: multiplies against amount to make larger values easier [OPTIONAL]
 *
 * Example:
 * ~~~
 *  qmove(4, 24)
 * ~~~
 * Will move that character 24 pixels to the left.
 * ----------------------------------------------------------------------------
 * **Arc**
 * ----------------------------------------------------------------------------
 * Arcing is used to make a character orbit around a position. Note that collisions
 * are ignored when arcing, but interactions still work. To add a arc add a script
 * in the move route in the format:
 * ~~~
 *  arc(PIVOTX, PIVOTY, RADIAN, CCWISE?, FRAMES)
 * ~~~
 * - PIVOTX: The x position to orbit around, in pixels
 * - PIVOTY: The y position to orbit around, in pixels
 * - RADIAN: The degrees to move, in radians
 * - CCWISE?: set to true or false; if true it will arc countclock wise
 * - FRAMES: The amount of frames to complete the arc
 *
 * Example:
 * ~~~
 *  arc(480,480,Math.PI*2,false,60)
 * ~~~
 * Will make the character do a full 360 arc clockwise around the point 480, 480
 * and it'll take 60 frames.
 * ============================================================================
 * ## Event Notetags/Comments
 * ============================================================================
 * **Offsets**
 * ----------------------------------------------------------------------------
 * To shift an events initial starting position, you can use the following
 * note tags:
 * ~~~
 *  <ox:X>
 * ~~~
 * or
 * ~~~
 *  <oy:X>
 * ~~~
 * Where X is the number of pixels to shift the event. Can be negative.
 * ----------------------------------------------------------------------------
 * **SmartDir**
 * ----------------------------------------------------------------------------
 * By default, when the player collides with an event it won't trigger the
 * Smart Move Dir effect. To enable this, add following notetag to the event:
 * ~~~
 *  <smartDir>
 * ~~~
 * ----------------------------------------------------------------------------
 * **IgnoreCharas**
 * ----------------------------------------------------------------------------
 * You can have an event ignore certain characters when collision checking. This
 * allows you to let some events move through some events or the player. Note that
 * this is not 2 ways, so if an event can move through the player, that doesn't
 * mean the player can move through the event.
 * ~~~
 *  <ignoreCharas:CHARAIDS>
 * ~~~
 * Where CHARAIDS is a list of character Ids, separated by a comma
 * ============================================================================
 * ## Map Notetags
 * ============================================================================
 * **GridSize**
 * ----------------------------------------------------------------------------
 * You can set the grid size for certain maps by using the notetag:
 * ~~~
 *  <grid:X>
 * ~~~
 * Where X is the grid size to use for this map.
 * ----------------------------------------------------------------------------
 * **OffGrid**
 * ----------------------------------------------------------------------------
 * You can set weither you can or can't move off the grid for certain maps by
 * using the notetag:
 * ~~~
 *  <offGrid:BOOL>
 * ~~~
 * Where BOOL is true or false
 * ----------------------------------------------------------------------------
 * **MidPass**
 * ----------------------------------------------------------------------------
 * You can set weither you want to use the mid pass function for certain maps by
 * using the notetag:
 * ~~~
 *  <midPass:BOOL>
 * ~~~
 * Where BOOL is true or false
 * ============================================================================
 * ## Plugin Commands
 * ============================================================================
 * **Transfer**
 * ----------------------------------------------------------------------------
 * MV event transfers are grid based. So this plugin command lets you map transfer
 * to a pixel x / y position.
 * ~~~
 *  qMovement transfer [MAPID] [X] [Y] [OPTIONS]
 * ~~~
 * - MAPID: The id of the map to transfer to
 * - X: The x position to transfer to, in pixels
 * - Y: The y position to transfer to, in pixels
 *
 * Possible options:
 *
 * - dirX: Set X to the dir to face after the transfer.
 *   - Can be 2, 4, 6, 8, or for diagonals 1, 3, 7, 9
 * - fadeBlack: Will fade black when transfering
 * - fadeWhite: Will fade white when transfering
 *
 * Example:
 * ~~~
 *  qMovement transfer 1 100 116 dir2 fadeBlack
 * ~~~
 * Will transfer the player to map 1 at x100, y116. There will be a black fade
 * and player will be facing down
 * ~~~
 *  qMovement transfer 1 100 116
 * ~~~
 * Will transfer the player to map 1 at x100, y116. There will be no fade and
 * players direction won't change
 * ----------------------------------------------------------------------------
 * **Set Pos**
 * ----------------------------------------------------------------------------
 * This command will let you move a character to a x / y pixel position. Note
 * this will not "walk" the character to that position! This will place the
 * character at this position, similar to a transfer.
 * ~~~
 *  qMovement setPos [CHARAID] [X] [Y] [OPTIONS]
 * ~~~
 * - CHARAID: The character identifier.
 *  - For player: 0, p, or player
 *  - For events: EVENTID, eEVENTID, eventEVENTID or this for the event that called this (replace EVENTID with a number)
 * - X: The x position to set to, in pixels
 * - Y: The y position to set to, in pixels
 *
 * Possible options:
 *
 * - dirX: Set X to the dir to face after the transfer.
 *   - Can be 2, 4, 6, 8, or for diagonals 1, 3, 7, 9
 *
 * ----------------------------------------------------------------------------
 * **Change Collider**
 * ----------------------------------------------------------------------------
 * This command will let you change a collider for a character. Note that you
 * should use this carefully. If you don't you can get that character stuck.
 * ~~~
 *  qMovement changeCollider [CHARAID] [TYPE] [SHAPE] [WIDTH] [HEIGHT] [OX] [OY]
 * ~~~
 * - CHARAID: The character identifier.
 *  - For player: 0, p, or player
 *  - For events: EVENTID, eEVENTID, eventEVENTID or this for the event that called this (replace EVENTID with a number)
 * - TYPE: The type of collider, set to default, collision or interaction
 * - SHAPE: Set to box or circle
 * - WIDTH: The width of the collider, in pixels
 * - HEIGHT: The height of the collider, in pixels
 * - OX: The X Offset of the collider, in pixels
 * - OY: The Y Offset of the collider, in pixels
 *
 * You can also set it to a preset by using the format:
 * ~~~
 *  qMovement changeCollider [CHARAID] [TYPE] preset [PRESETID]
 * ~~~
 * - CHARAID: The character identifier.
 *  - For player: 0, p, or player
 *  - For events: EVENTID, eEVENTID, eventEVENTID or this for the event that called this (replace EVENTID with a number)
 * - TYPE: The type of collider, set to default, collision or interaction
 * - PRESETID: The PresetID you set in the preset parameter.
 * ============================================================================
 * ## Tips
 * ============================================================================
 * **No closed open spaces!**
 * ----------------------------------------------------------------------------
 * For performance reasons, you should try to avoid having open spaces that are
 * closed off.
 *
 * ![Example](https://quxios.github.io/imgs/qmovement/openSpaces.png)
 *
 * On the left we can see some tiles that have a collider border, but their inside
 * is "open". This issue is should be corrected when using QPathfind because
 * if someone was to click inside that "open" space, it is passable and QPathfind
 * will try to find a way in even though there is no way in and will cause massive
 * lag. The fix can be pretty simple, you could add a CollisionMap (though that
 * may be another issue in its own) or add a RegionCollider to fill up the full
 * tile like I did on the correct side of that image.
 * ----------------------------------------------------------------------------
 * **Collision Maps - Heavy**
 * ----------------------------------------------------------------------------
 * Try to use collision maps only if you absolutely need to. Collision maps
 * can be very large images which will make your game use more memory and can
 * cause some slower pcs to start lagging. The collision checking for collision
 * maps are also take about 2-4x more time to compute and is a lot less accurate
 * since it only checks if the colliders edge collided with the collision map.
 * So using collision maps, might be pretty, but use it with caution as it can
 * slow down your game! A better solution for this would be to use a PolygonMap
 * where you create polygon colliders and add them into the map.
 * ============================================================================
 * ## Addons
 * ============================================================================
 * **Pathfind**
 * ----------------------------------------------------------------------------
 * https://quxios.github.io/plugins/QPathfind
 *
 * QPathfind is an A* pathfinding algorithm. This algorithm can be pretty heavy
 * if you are doing pixel based movements. So avoid having to many pathfinders
 * running at the same time.
 *
 * For the interval settings, you want to set this to a value where the path
 * can be found in 1-3 frames. You can think of intervals as the number of
 * moves to try per frame. The default setting 100, is good for grid based
 * since that will take you 100 grid spaces away. But for a pixel based, 100
 * steps might not be as far. If most of your pathfinds will be short (paths less then
 * 10 tiles away), then you should set this to a value between 100-300. For medium
 * paths (10-20 tiles away) try a value between 300-700. For large or complicated
 * paths (20+ tiles away or lots of obsticles) try something between 1000-2000.
 * I would avoid going over 2000. My opinion is to keep it below 1000, and simplify
 * any of your larger paths by either splitting it into multiple pathfinds or
 * just making the path less complex.
 *
 * ----------------------------------------------------------------------------
 * **Collision Map**
 * ----------------------------------------------------------------------------
 * https://quxios.github.io/plugins/QM+CollisionMap
 *
 * Collision Map is an addon for this plugin that lets you use images for
 * collisions. Note that collision map checks are a lot heavier then normal
 * collision checks. So this plugin can make your game laggier if used with
 * other heavy plugins.
 *
 * ----------------------------------------------------------------------------
 * **Region Colliders**
 * ----------------------------------------------------------------------------
 * https://quxios.github.io/plugins/QM+RegionColliders
 *
 * Region Colliders is an addon for this plugin that lets you add colliders
 * to regions by creating a json file.
 * ============================================================================
 * ## Showcase
 * ============================================================================
 * This section is for user created stuff. If you created a video, game, tutorial,
 * or an addon for QMovement feel free to send me a link and I'll showcase it here!
 * ----------------------------------------------------------------------------
 * **Videos**
 * ----------------------------------------------------------------------------
 * Great example of using the collision map addon:
 *
 * https://www.youtube.com/watch?v=-BN4Pyr5IBo
 *
 * ============================================================================
 * ## Links
 * ============================================================================
 * Formated Help:
 *
 *  https://quxios.github.io/plugins/QMovement
 *
 * RPGMakerWebs:
 *
 *  http://forums.rpgmakerweb.com/index.php?threads/qplugins.73023/
 *
 * Terms of use:
 *
 *  https://github.com/quxios/QMV-Master-Demo/blob/master/readme.md
 *
 * Like my plugins? Support me on Patreon!
 *
 *  https://www.patreon.com/quxios
 *
 * @tags movement, pixel, character
 */
 /*~struct~Collider:
 * @param Type
 * @desc Set to box or circle
 * @type select
 * @option Box
 * @value box
 * @option Circle
 * @value circle
 * @default box
 *
 * @param Width
 * @desc Set to the width of the collider.
 * @type Number
 * @default 36
 *
 * @param Height
 * @desc Set to the height of the collider.
 * @type Number
 * @default 24
 *
 * @param Offset X
 * @desc Set to the x offset of the collider.
 * @type Number
 * @min -9999
 * @default 6
 *
 * @param Offset Y
 * @desc Set to the y offset of the collider.
 * @type Number
 * @min -9999
 * @default 24
 */
 /*~struct~ColliderPreset:
 * @param ID
 * @desc The ID of this preset, needs to be unique!
 * @default
 *
 * @param Type
 * @desc Set to box or circle
 * @type select
 * @option Box
 * @value box
 * @option Circle
 * @value circle
 * @default box
 *
 * @param Width
 * @desc Set to the width of the collider.
 * @type Number
 * @default 36
 *
 * @param Height
 * @desc Set to the height of the collider.
 * @type Number
 * @default 24
 *
 * @param Offset X
 * @desc Set to the x offset of the collider.
 * @type Number
 * @default 6
 *
 * @param Offset Y
 * @desc Set to the y offset of the collider.
 * @type Number
 * @default 24
 */
//=============================================================================
//=============================================================================
// QMovement Static Class

function QMovement() {
  throw new Error('This is a static class');
}

(function() {
  var _PARAMS = QPlus.getParams('<QMovement>', {
    'Player Collider': {
      "Type": "box", "Width": 36,"Height":24 ,"Offset X": 6,"Offset Y": 24
    },
    'Event Collider': {
      "Type": "box", "Width": 36,"Height":24 ,"Offset X": 6,"Offset Y": 24
    },
    'Presets': []
  });

  QMovement.grid = _PARAMS['Grid'];
  QMovement.tileSize = _PARAMS['Tile Size'];
  QMovement.offGrid = _PARAMS['Off Grid'];
  QMovement.smartMove = _PARAMS['Smart Move'];
  QMovement.midPass = _PARAMS['Mid Pass'];
  QMovement.moveOnClick = _PARAMS['Move on click'];
  QMovement.diagonal = _PARAMS['Diagonal'];
  QMovement.collision = '#FF0000'; // will be changable in a separate addon
  QMovement.water1 = '#00FF00'; // will be changable in a separate addon
  QMovement.water2 = '#0000FF'; // will be changable in a separate addon
  QMovement.water1Tag = 1; // will be changable in a separate addon
  QMovement.water2Tag = 2; // will be changable in a separate addon
  QMovement.playerCollider = convertColliderStruct(_PARAMS['Player Collider']);
  QMovement.eventCollider = convertColliderStruct(_PARAMS['Event Collider']);
  QMovement.presets = {};
  _PARAMS['Presets'].forEach(function(preset) {
    QMovement.presets[preset.ID] = convertColliderStruct(preset);
  });
  QMovement.showColliders = _PARAMS['Show Colliders'];
  QMovement.tileBoxes = {
    1537: [48, 6, 0, 42],
    1538: [6, 48],
    1539: [[48, 6, 0, 42], [6, 48]],
    1540: [6, 48, 42],
    1541: [[48, 6, 0, 42], [6, 48, 42]],
    1542: [[6, 48], [6, 48, 42]],
    1543: [[48, 6, 0, 42], [6, 48], [6, 48, 42]],
    1544: [48, 6],
    1545: [[48, 6], [48, 6, 0, 42]],
    1546: [[48, 6], [6, 48]],
    1547: [[48, 6], [48, 6, 0, 42], [6, 48]],
    1548: [[48, 6], [6, 48, 42]],
    1549: [[48, 6], [48, 6, 0, 42], [6, 48, 42]],
    1550: [[48, 6], [6, 48], [6, 48, 42]],
    1551: [48, 48], // Impassable A5, B
    2063: [48, 48], // Impassable A1
    2575: [48, 48],
    3586: [6, 48],
    3588: [6, 48, 42],
    3590: [[6, 48], [6, 48, 42]],
    3592: [48, 6],
    3594: [[48, 6], [6, 48]],
    3596: [[48, 6], [6, 48, 42]],
    3598: [[48, 6], [6, 48], [6, 48, 42]],
    3599: [48, 48],  // Impassable A2, A3, A4
    3727: [48, 48]
  };
  var rs = QMovement.tileSize / 48;
  for (var key in QMovement.tileBoxes) {
    if (QMovement.tileBoxes.hasOwnProperty(key)) {
      for (var i = 0; i < QMovement.tileBoxes[key].length; i++) {
        if (QMovement.tileBoxes[key][i].constructor === Array) {
          for (var j = 0; j < QMovement.tileBoxes[key][i].length; j++) {
            QMovement.tileBoxes[key][i][j] *= rs;
          }
        } else {
          QMovement.tileBoxes[key][i] *= rs;
        }
      }
    }
  }
  // following will be changable in a separate addon
  QMovement.regionColliders = {};
  QMovement.colliderMap = {};

  function convertColliderStruct(struct) {
    return [
      struct.Type,
      struct.Width,
      struct.Height,
      struct['Offset X'],
      struct['Offset Y']
    ]
  }
})();

//=============================================================================
// Colliders

//-----------------------------------------------------------------------------
// Polygon_Collider

function Polygon_Collider() {
  this.initialize.apply(this, arguments);
}

(function() {
  Polygon_Collider._counter = 0;

  Polygon_Collider.prototype.initialize = function(points) {
    var args = [];
    for (var i = 1; i < arguments.length; i++) {
      args.push(arguments[i]);
    }
    this.initMembers.apply(this, args);
    this.makeVertices(points);
  };

  Polygon_Collider.prototype.initMembers = function(x, y) {
    x = x !== undefined ? x : 0;
    y = y !== undefined ? y : 0;
    this._position = new Point(x, y);
    this._scale = new Point(1, 1);
    this._offset = new Point(0, 0);
    this._pivot = new Point(0, 0);
    this._radian = 0;
    this._note = '';
    this.meta = {};
    this.id = Polygon_Collider._counter++;
  };

  Object.defineProperty(Polygon_Collider.prototype, 'note', {
    get() {
      return this._note;
    },
    set(note) {
      this._note = note;
      this.meta = QPlus.getMeta(note);
    }
  });

  Object.defineProperty(Polygon_Collider.prototype, 'x', {
    get() {
      return this._position.x;
    },
    set(x) {
      this._position.x = x;
    }
  });

  Object.defineProperty(Polygon_Collider.prototype, 'y', {
    get() {
      return this._position.y;
    },
    set(y) {
      this._position.y = y;
    }
  });

  Object.defineProperty(Polygon_Collider.prototype, 'ox', {
    get() {
      return this._offset.x + this._pivot.x;
    },
    set(value) {
      this._offset.x = value;
      this.refreshVertices();
    }
  });

  Object.defineProperty(Polygon_Collider.prototype, 'oy', {
    get() {
      return this._offset.y + this._pivot.y;
    },
    set(value) {
      this._offset.y = value - this._pivot.y;
      this.refreshVertices();
    }
  });

  Polygon_Collider.prototype.isPolygon = function() {
    return true;
  };

  Polygon_Collider.prototype.isBox = function() {
    return true;
  };

  Polygon_Collider.prototype.isCircle = function() {
    return false;
  };

  Polygon_Collider.prototype.makeVertices = function(points) {
    this._vertices = [];
    this._baseVertices = [];
    this._edges = [];
    this._vectors = [];
    this._xMin = null;
    this._xMax = null;
    this._yMin = null;
    this._yMax = null;
    for (var i = 0; i < points.length; i++) {
      var x = points[i].x - this._pivot.x;
      var y = points[i].y - this._pivot.y;
      var x2 = x + this.x + this.ox;
      var y2 = y + this.y + this.oy;
      this._vertices.push(new Point(x2, y2));
      this._baseVertices.push(new Point(x, y));
      if (i !== 0) {
        var prev = this._vertices[i - 1];
        this._edges.push({
          x1: prev.x, x2: x2,
          y1: prev.y, y2: y2
        })
      }
      if (i === points.length - 1) {
        var first = this._vertices[0];
        this._edges.push({
          x1: x2, x2: first.x,
          y1: y2, y2: first.y
        })
      }
      var radian = Math.atan2(y, x);
      radian += radian < 0 ? Math.PI * 2 : 0;
      var dist = Math.sqrt(x * x + y * y);
      this._vectors.push({ radian, dist });
      if (this._xMin === null || this._xMin > x) {
        this._xMin = x;
      }
      if (this._xMax === null || this._xMax < x) {
        this._xMax = x;
      }
      if (this._yMin === null || this._yMin > y) {
        this._yMin = y;
      }
      if (this._yMax === null || this._yMax < y) {
        this._yMax = y;
      }
    }
    this.width = Math.abs(this._xMax - this._xMin);
    this.height = Math.abs(this._yMax - this._yMin);
    var x1 = this._xMin + this.x + this.ox;
    var y1 = this._yMin + this.y + this.oy;
    this.center = new Point(x1 + this.width / 2, y1 + this.height / 2);
  };

  Polygon_Collider.prototype.makeVectors = function() {
    this._vectors = this._baseVertices.map((function(vertex) {
      var dx = vertex.x - this._pivot.x;
      var dy = vertex.y - this._pivot.y;
      var radian = Math.atan2(dy, dx);
      radian += radian < 0 ? Math.PI * 2 : 0;
      var dist = Math.sqrt(dx * dx + dy * dy);
      return { radian, dist };
    }).bind(this));
  };

  Polygon_Collider.prototype.setBounds = function() {
    this._xMin = null;
    this._xMax = null;
    this._yMin = null;
    this._yMax = null;
    for (var i = 0; i < this._baseVertices.length; i++) {
      var x = this._baseVertices[i].x;
      var y = this._baseVertices[i].y;
      if (this._xMin === null || this._xMin > x) {
        this._xMin = x;
      }
      if (this._xMax === null || this._xMax < x) {
        this._xMax = x;
      }
      if (this._yMin === null || this._yMin > y) {
        this._yMin = y;
      }
      if (this._yMax === null || this._yMax < y) {
        this._yMax = y;
      }
    }
    this.width = Math.abs(this._xMax - this._xMin);
    this.height = Math.abs(this._yMax - this._yMin);
    var x1 = this._xMin + this.x + this.ox;
    var y1 = this._yMin + this.y + this.oy;
    this.center = new Point(x1 + this.width / 2, y1 + this.height / 2);
  };

  Polygon_Collider.prototype.refreshVertices = function() {
    this._edges = [];
    var i, j;
    for (i = 0, j = this._vertices.length; i < j; i++) {
      var vertex = this._vertices[i];
      vertex.x = this.x + this._baseVertices[i].x + this.ox;
      vertex.y = this.y + this._baseVertices[i].y + this.oy;
      if (i !== 0) {
        var prev = this._vertices[i - 1];
        this._edges.push({
          x1: prev.x, x2: vertex.x,
          y1: prev.y, y2: vertex.y
        })
      }
      if (i === j - 1) {
        var first = this._vertices[0];
        this._edges.push({
          x1: vertex.x, x2: first.x,
          y1: vertex.y, y2: first.y
        })
      }
    }
    this.setBounds();
  };

  Polygon_Collider.prototype.sectorEdge = function() {
    var x1 = this._xMin + this.x + this.ox;
    var x2 = this._xMax + this.x + this.ox - 1;
    var y1 = this._yMin + this.y + this.oy;
    var y2 = this._yMax + this.y + this.oy - 1;
    x1 = Math.floor(x1 / ColliderManager._sectorSize);
    x2 = Math.floor(x2 / ColliderManager._sectorSize);
    y1 = Math.floor(y1 / ColliderManager._sectorSize);
    y2 = Math.floor(y2 / ColliderManager._sectorSize);
    return {
      x1: x1, x2: x2,
      y1: y1, y2: y2
    }
  };

  Polygon_Collider.prototype.gridEdge = function() {
    var x1 = this._xMin + this.x + this.ox;
    var x2 = this._xMax + this.x + this.ox - 1;
    var y1 = this._yMin + this.y + this.oy;
    var y2 = this._yMax + this.y + this.oy - 1;
    x1 = Math.floor(x1 / QMovement.tileSize);
    x2 = Math.floor(x2 / QMovement.tileSize);
    y1 = Math.floor(y1 / QMovement.tileSize);
    y2 = Math.floor(y2 / QMovement.tileSize);
    return {
      x1: x1, x2: x2,
      y1: y1, y2: y2
    }
  };

  Polygon_Collider.prototype.edge = function() {
    var x1 = this._xMin + this.x + this.ox;
    var x2 = this._xMax + this.x + this.ox - 1;
    var y1 = this._yMin + this.y + this.oy;
    var y2 = this._yMax + this.y + this.oy - 1;
    return {
      x1: x1, x2: x2,
      y1: y1, y2: y2
    }
  };

  Polygon_Collider.prototype.setPivot = function(x, y) {
    this._pivot.x = x;
    this._pivot.y = y;
    this.makeVectors();
    this.rotate(0); // Resets base vertices
  };

  Polygon_Collider.prototype.centerPivot = function() {
    this._pivot.x = this.width / 2;
    this._pivot.y = this.height / 2;
    this.makeVectors();
    this.rotate(0); // Resets base vertices
  };

  Polygon_Collider.prototype.setRadian = function(radian) {
    radian = radian !== undefined ? radian : 0;
    this.rotate(radian - this._radian);
  };

  Polygon_Collider.prototype.rotate = function(radian) {
    this._radian += radian;
    for (var i = 0; i < this._vectors.length; i++) {
      var vector = this._vectors[i];
      vector.radian += radian;
      var x = vector.dist * Math.cos(vector.radian);
      var y = vector.dist * Math.sin(vector.radian);
      this._baseVertices[i].x = Math.round(x);
      this._baseVertices[i].y = Math.round(y);
    }
    this.refreshVertices();
  };

  Polygon_Collider.prototype.setScale = function(zX, zY) {
    zX = zX !== undefined ? zX : 1;
    zY = zY !== undefined ? zY : 1;
    this.scale(zX / this._scale.x, zY / this._scale.y);
  };

  Polygon_Collider.prototype.scale = function(zX, zY) {
    this._scale.x *= zX;
    this._scale.y *= zY;
    for (var i = 0; i < this._vectors.length; i++) {
      var vector = this._vectors[i];
      var x = vector.dist * Math.cos(vector.radian);
      var y = vector.dist * Math.sin(vector.radian);
      x *= zX;
      y *= zY;
      vector.radian = Math.atan2(y, x);
      vector.radian += vector.radian < 0 ? Math.PI * 2 : 0;
      vector.dist = Math.sqrt(x * x + y * y);
      this._baseVertices[i].x = Math.round(x);
      this._baseVertices[i].y = Math.round(y);
    }
    this.refreshVertices();
  };

  Polygon_Collider.prototype.moveTo = function(x, y) {
    if (x !== this.x || y !== this.y) {
      this.x = x;
      this.y = y;
      this.refreshVertices();
    }
  };

  Polygon_Collider.prototype.intersects = function(other) {
    if (this.height === 0 || this.width === 0) return false;
    if (other.height === 0 || other.width === 0) return false;
    if (!other.isPolygon()) {
      if (this.containsPoint(other.center.x, other.center.y)) return true;
    }
    if (!this.isPolygon()) {
      if (other.containsPoint(this.center.x, this.center.y)) return true;
    }
    var i, j, x, y;
    for (i = 0, j = other._vertices.length; i < j; i++) {
      x = other._vertices[i].x;
      y = other._vertices[i].y;
      if (this.containsPoint(x, y)) return true;
    }
    for (i = 0, j = this._vertices.length; i < j; i++) {
      x = this._vertices[i].x;
      y = this._vertices[i].y;
      if (other.containsPoint(x, y)) return true;
    }
    // TODO add edge checking
    /*
    for (i = 0; i < this._edges.length; i++) {
      for (j = 0; j < other._edges.length; j++) {

      }
    }*/
    return false;
  };

  Polygon_Collider.prototype.inside = function(other) {
    if (this.height === 0 || this.width === 0) return false;
    if (other.height === 0 || other.width === 0) return false;
    var i, j, x, y;
    for (i = 0, j = other._vertices.length; i < j; i++) {
      x = other._vertices[i].x;
      y = other._vertices[i].y;
      if (!this.containsPoint(x, y)) {
        return false;
      }
    }
    return true;
  };

  Polygon_Collider.prototype.containsPoint = function(x, y) {
    var i;
    var j = this._vertices.length - 1;
    var odd = false;
    var poly = this._vertices;
    for (i = 0; i < this._vertices.length; i++) {
      if (poly[i].y < y && poly[j].y >= y || poly[j].y < y && poly[i].y >= y) {
        if (poly[i].x + (y - poly[i].y) / (poly[j].y - poly[i].y) * (poly[j].x - poly[i].x) < x) {
          odd = !odd;
        }
      }
      j = i;
    }
    return odd;
  };

  Polygon_Collider.prototype.lineIntersection = function(lineA, lineB) {
    var a1 = lineA.y1 - lineA.y2;
    var b1 = lineA.x2 - lineA.x1;
    var a2 = lineB.y1 - lineB.y2;
    var b2 = lineB.x2 - lineB.x1;
    var det = a1 * b2 - a2 * b1;
    if (det == 0) {
      return false;
    }
    var c1 = a1 * lineA.x1 + b1 * lineA.y1;
    var c2 = a2 * lineB.x1 + b2 * lineB.y1;
    var x = (b2 * c1 - b1 * c2) / det;
    var y = (a1 * c2 - a2 * c1) / det;
    // incomplete
    // returns false if lines don't intersect
    // x/y will return where or when they will intersect
    return new Point(x, y);
  };

  // TODO Optimize this
  // Compaire other methods, example atan2 - atan2 or a dot product
  Polygon_Collider.prototype.bestPairFrom = function(point) {
    var vertices = this._vertices;
    var radians = [];
    var points = [];
    for (var i = 0; i < vertices.length; i++) {
      var radian = Math.atan2(vertices[i].y - point.y, vertices[i].x - point.x);
      radian += radian < 0 ? 2 * Math.PI : 0;
      radians.push(radian);
      points.push(new Point(vertices[i].x, vertices[i].y));
    }
    var bestPair = [];
    var currI = 0;
    var max = -Math.PI * 2;
    while (points.length > 0) {
      var curr = points.shift();
      for (var i = 0; i < points.length; i++) {
        var dr = radians[currI] - radians[currI + i + 1];
        if (Math.abs(dr) > max) {
          max = Math.abs(dr);
          bestPair = [currI, currI + i + 1];
        }
      }
      currI++;
    }
    return bestPair;
  };

  // returns a new polygon
  Polygon_Collider.prototype.stretchedPoly = function(radian, dist) {
    var dist2 = dist + Math.max(this.width, this.height);
    var xComponent = Math.cos(radian) * dist;
    var yComponent = Math.sin(radian) * dist;
    var x1 = this.center.x + Math.cos(radian) * dist2;
    var y1 = this.center.y + Math.sin(radian) * dist2;
    var bestPair = this.bestPairFrom(new Point(x1, y1));
    var vertices = this._vertices;
    var pointsA = [];
    var pointsB = [];
    var i;
    for (i = 0; i < vertices.length; i++) {
      var x2 = vertices[i].x - this.x;
      var y2 = vertices[i].y - this.y;
      pointsA.push(new Point(x2, y2));
      pointsB.push(new Point(x2 + xComponent, y2 + yComponent));
    }
    // TODO add the other vertices from collider
    var points = [];
    points.push(pointsA[bestPair[0]]);
    points.push(pointsB[bestPair[0]]);
    points.push(pointsB[bestPair[1]]);
    points.push(pointsA[bestPair[1]]);
    return new Polygon_Collider(points, this.x, this.y);
  };
})();

//-----------------------------------------------------------------------------
// Box_Collider

function Box_Collider() {
  this.initialize.apply(this, arguments);
}

(function() {
  Box_Collider.prototype = Object.create(Polygon_Collider.prototype);
  Box_Collider.prototype.constructor = Box_Collider;

  Box_Collider.prototype.initialize = function(width, height, ox, oy, options) {
    var points = [
      new Point(0, 0),
      new Point(width, 0),
      new Point(width, height),
      new Point(0, height)
    ];
    Polygon_Collider.prototype.initialize.call(this, points, width, height, ox, oy, options);
  };

  Box_Collider.prototype.initMembers = function(width, height, ox, oy, options) {
    Polygon_Collider.prototype.initMembers.call(this, 0, 0);
    ox = ox === undefined ? 0 : ox;
    oy = oy === undefined ? 0 : oy;
    options = options === undefined ? {} : options;
    this._offset = new Point(ox, oy);
    this._pivot = options.pivot || new Point(width / 2, height / 2);
    this._scale = options.scale || this._scale;
    this._radian = options.radian || this._radian;
    this._position = options.position || this._position;
  };

  Box_Collider.prototype.isPolygon = function() {
    return false;
  };

  Box_Collider.prototype.isBox = function() {
    return true;
  };

  Box_Collider.prototype.containsPoint = function(x, y) {
    if (this._radian === 0) {
      var xMin = this._xMin + this.x + this.ox;
      var xMax = this._xMax + this.x + this.ox;
      var yMin = this._yMin + this.y + this.oy;
      var yMax = this._yMax + this.y + this.oy;
      var insideX = x >= xMin && x <= xMax;
      var insideY = y >= yMin && y <= yMax;
      return insideX && insideY;
    } else {
      return Polygon_Collider.prototype.containsPoint.call(this, x, y);
    }
  };

})();

//-----------------------------------------------------------------------------
// Circle_Collider

function Circle_Collider() {
  this.initialize.apply(this, arguments);
}

(function() {
  Circle_Collider.prototype = Object.create(Polygon_Collider.prototype);
  Circle_Collider.prototype.constructor = Circle_Collider;

  Circle_Collider.prototype.initialize = function(width, height, ox, oy, options) {
    this._radius = new Point(width / 2, height / 2);
    var points = [];
    for (var i = 7; i >= 0; i--) {
      var rad = Math.PI / 4 * i + Math.PI;
      var x = this._radius.x + this._radius.x * Math.cos(rad);
      var y = this._radius.y + this._radius.y * -Math.sin(rad);
      points.push(new Point(x, y));
    }
    Polygon_Collider.prototype.initialize.call(this, points, width, height, ox, oy, options);
  };

  Circle_Collider.prototype.initMembers = Box_Collider.prototype.initMembers;

  Object.defineProperty(Circle_Collider.prototype, 'radiusX', {
    get() {
      return this._radius.x;
    }
  });

  Object.defineProperty(Circle_Collider.prototype, 'radiusY', {
    get() {
      return this._radius.y;
    }
  });

  Circle_Collider.prototype.isPolygon = function() {
    return false;
  };

  Circle_Collider.prototype.isCircle = function() {
    return true;
  };

  Circle_Collider.prototype.scale = function(zX, zY) {
    Polygon_Collider.prototype.scale.call(this, zX, zY);
    this._radius.x *= zX;
    this._radius.y *= zY;
  };

  Circle_Collider.prototype.circlePosition = function(radian) {
    var x = this.radiusX * Math.cos(radian);
    var y = this.radiusY * -Math.sin(radian);
    var dist = Math.sqrt(x * x + y * y);
    radian -= this._radian;
    x = dist * Math.cos(radian);
    y = dist * -Math.sin(radian);
    return new Point(this.center.x + x, this.center.y + y);
  };

  Circle_Collider.prototype.intersects = function(other) {
    if (this.height === 0 || this.width === 0) return false;
    if (other.height === 0 || other.width === 0) return false;
    if (this.containsPoint(other.center.x, other.center.y)) return true;
    if (other.containsPoint(this.center.x, this.center.y)) return true;
    var x1 = this.center.x;
    var x2 = other.center.x;
    var y1 = this.center.y;
    var y2 = other.center.y;
    var rad = Math.atan2(y1 - y2, x2 - x1);
    rad += rad < 0 ? 2 * Math.PI : 0;
    var pos = this.circlePosition(rad);
    if (other.containsPoint(pos.x, pos.y)) return true;
    if (other.isCircle()) {
      rad = Math.atan2(y2 - y1, x1 - x2);
      rad += rad < 0 ? 2 * Math.PI : 0;
      pos = other.circlePosition(rad);
      if (this.containsPoint(pos.x, pos.y)) return true;
    }
    var i, j;
    for (i = 0, j = other._vertices.length; i < j; i++) {
      x1 = other._vertices[i].x;
      y1 = other._vertices[i].y;
      if (this.containsPoint(x1, y1)) return true;
    }
    for (i = 0, j = this._vertices.length; i < j; i++) {
      x1 = this._vertices[i].x;
      y1 = this._vertices[i].y;
      if (other.containsPoint(x1, y1)) return true;
    }
    return false;
  };
})();

//-----------------------------------------------------------------------------
// ColliderManager

function ColliderManager() {
  throw new Error('This is a static class');
}

(function() {
  ColliderManager._colliders = [];
  ColliderManager._colliderGrid = [];
  ColliderManager._characterGrid = [];
  ColliderManager._sectorSize = QMovement.tileSize;
  ColliderManager._needsRefresh = true;
  ColliderManager.container = new Sprite();
  ColliderManager.container.alpha = 0.3;
  ColliderManager.containerDict = {};
  ColliderManager.visible = QMovement.showColliders;

  ColliderManager.clear = function() {
    this._colliders = [];
    this._colliderGrid = [];
    this._characterGrid = [];
    this.container.removeChildren();
    this.containerDict = {};
  };

  ColliderManager.refresh = function() {
    this.clear();
    this._colliderGrid = new Array(this._mapWidth);
    for (var x = 0; x < this._colliderGrid.length; x++) {
      this._colliderGrid[x] = [];
      for (var y = 0; y < this._mapHeight; y++) {
        this._colliderGrid[x].push([]);
      }
    }
    this._characterGrid = new Array(this._mapWidth);
    for (var x = 0; x < this._characterGrid.length; x++) {
      this._characterGrid[x] = [];
      for (var y = 0; y < this._mapHeight; y++) {
        this._characterGrid[x].push([]);
      }
    }
    this._needsRefresh = false;
  };

  ColliderManager.addCollider = function(collider, duration, ignoreGrid) {
    if (!$dataMap) return;
    var i = this._colliders.indexOf(collider);
    if (i === -1) {
      this._colliders.push(collider);
      if (duration > 0 || duration === -1) {
        this.draw(collider, duration);
      }
    }
    if (!ignoreGrid) {
      this.updateGrid(collider);
    }
  };

  ColliderManager.addCharacter = function(character, duration) {
    if (!$dataMap) return;
    var i = this._colliders.indexOf(character);
    if (i === -1) {
      this._colliders.push(character);
      if (duration > 0 || duration === -1) {
        this.draw(character.collider('bounds'), duration);
      }
    }
    this.updateGrid(character);
  };

  ColliderManager.remove = function(collider) {
    var i = this._colliders.indexOf(collider);
    if (i < 0) return;
    this.removeFromGrid(collider);
    if (!collider._colliders) collider.kill = true;
    this._colliders.splice(i, 1);
  };

  ColliderManager.removeSprite = function(sprite) {
    this.container.removeChild(sprite);
    delete this.containerDict[sprite._collider.id];
  };

  ColliderManager.updateGrid = function(collider, prevGrid) {
    if (this._needsRefresh) return;
    var currGrid;
    var grid;
    if (collider._colliders) {
      grid = this._characterGrid;
      currGrid = collider.collider('bounds').sectorEdge();
    } else {
      grid = this._colliderGrid;
      currGrid = collider.sectorEdge();
    }
    // TODO make this into 1 single 2d loop
    var x, y;
    if (prevGrid) {
      if (currGrid.x1 == prevGrid.x1 && currGrid.y1 === prevGrid.y1 &&
        currGrid.x2 == prevGrid.x2 && currGrid.y2 === prevGrid.y2) {
        return;
      }
      for (x = prevGrid.x1; x <= prevGrid.x2; x++) {
        for (y = prevGrid.y1; y <= prevGrid.y2; y++) {
          if (!grid[x] || !grid[x][y]) continue;
          var i = grid[x][y].indexOf(collider);
          if (i !== -1) {
            grid[x][y].splice(i, 1);
          }
        }
      }
    }
    for (x = currGrid.x1; x <= currGrid.x2; x++) {
      for (y = currGrid.y1; y <= currGrid.y2; y++) {
        if (!grid[x] || !grid[x][y]) continue;
        grid[x][y].push(collider);
      }
    }
  };

  ColliderManager.removeFromGrid = function(collider) {
    var grid;
    var edge;
    if (collider._colliders) { // Is a character obj
      grid = this._characterGrid;
      edge = collider.collider('bounds').sectorEdge();
    } else { // is a collider
      grid = this._colliderGrid;
      edge = collider.sectorEdge();
    }
    for (var x = edge.x1; x <= edge.x2; x++) {
      for (var y = edge.y1; y <= edge.y2; y++) {
        if (!grid[x] || !grid[x][y]) continue;
        var i = grid[x][y].indexOf(collider);
        if (i !== -1) {
          grid[x][y].splice(i, 1);
        }
      }
    }
  };

  ColliderManager.getCharactersNear = function(collider, only) {
    var grid = collider.sectorEdge();
    var near = [];
    var checked = {};
    var x, y, i;
    for (x = grid.x1; x <= grid.x2; x++) {
      for (y = grid.y1; y <= grid.y2; y++) {
        if (x < 0 || x >= this.sectorCols()) continue;
        if (y < 0 || y >= this.sectorRows()) continue;
        var charas = this._characterGrid[x][y];
        for (i = 0; i < charas.length; i++) {
          if (checked[charas[i].charaId()]) {
            continue;
          }
          checked[charas[i].charaId()] = true;
          if (only) {
            var value = only(charas[i]);
            if (value === 'break') {
              near.push(charas[i]);
              isBreaking = true;
              return near;
            } else if (value === false) {
              continue;
            }
          }
          near.push(charas[i]);
        }
      }
    }
    return near;
  };

  ColliderManager.getCollidersNear = function(collider, only, debug) {
    var grid = collider.sectorEdge();
    var near = [];
    var checked = {};
    var isBreaking = false;
    var x, y, i;
    for (x = grid.x1; x <= grid.x2; x++) {
      for (y = grid.y1; y <= grid.y2; y++) {
        if (x < 0 || x >= this.sectorCols()) continue;
        if (y < 0 || y >= this.sectorRows()) continue;
        var colliders = this._colliderGrid[x][y];
        for (i = 0; i < colliders.length; i++) {
          if (checked[colliders[i].id]) {
            continue;
          }
          checked[colliders[i].id] = true;
          if (only) {
            var value = only(colliders[i]);
            if (value === 'break') {
              near.push(colliders[i]);
              isBreaking = true;
              break;
            } else if (value === false) {
              continue;
            }
          }
          near.push(colliders[i]);
        }
        if (isBreaking) break;
      }
      if (isBreaking) break;
    }
    only = null;
    return near;
  };

  ColliderManager.getAllNear = function(collider, only) {
    var grid = collider.sectorEdge();
    var near = [];
    var checked = {};
    var x, y, i;
    for (x = grid.x1; x <= grid.x2; x++) {
      for (y = grid.y1; y <= grid.y2; y++) {
        if (x < 0 || x >= this.sectorCols()) continue;
        if (y < 0 || y >= this.sectorRows()) continue;
        var charas = this._characterGrid[x][y];
        var colliders = this._colliderGrid[x][y];
        for (i = 0; i < charas.length + colliders.length; i++) {
          var type = i >= charas.length ? 'collider' : 'chara';
          var obj;
          if (type === 'chara') {
            obj = charas[i];
            if (checked[obj.charaId()]) {
              continue;
            }
            checked[obj.charaId()] = true;
          } else {
            obj = colliders[i - charas.length];
            if (checked[obj.id]) {
              continue;
            }
            checked[obj.id] = true;
          }
          if (only) {
            var value = only(type, obj);
            if (value === 'break') {
              near.push(obj);
              return near;
            } else if (value === false) {
              continue;
            }
          }
          near.push(obj);
        }
      }
    }
    return near;
  };

  ColliderManager.sectorCols = function() {
    return Math.floor(this._mapWidth * QMovement.tileSize / this._sectorSize);
  };

  ColliderManager.sectorRows = function() {
    return Math.floor(this._mapHeight * QMovement.tileSize / this._sectorSize);
  };

  ColliderManager.draw = function(collider, duration) {
    if ($gameTemp.isPlaytest()) {
      if (this.containerDict[collider.id]) {
        this.containerDict[collider.id]._collider = collider;
        this.containerDict[collider.id]._collider.kill = false;
        this.containerDict[collider.id]._duration = duration;
        this.containerDict[collider.id].checkChanges();
        return;
      }
      collider.kill = false;
      var sprite = new Sprite_Collider(collider, duration || -1);
      this.container.addChild(sprite);
      this.containerDict[collider.id] = sprite;
    }
  };

  ColliderManager.update = function() {
    if (this.visible) {
      this.show();
    } else {
      this.hide();
    }
  };

  ColliderManager.toggle = function() {
    this.visible = !this.visible;
  };

  ColliderManager.show = function() {
    this.container.visible = true;
  };

  ColliderManager.hide = function() {
    this.container.visible = false;
  };

  ColliderManager.convertToCollider = function(arr) {
    var type = arr[0].toLowerCase();
    if (type === 'preset') {
      var arr = QMovement.presets[arr[1]];
      if (!arr) {
        alert("ERROR: Tried to use a collider preset that doesn't exist: ", type);
        return null;
      }
      type = arr[0].toLowerCase();
    }
    var w = arr[1] || 0;
    var h = arr[2] || 0;
    var ox = arr[3] || 0;
    var oy = arr[4] || 0;
    var collider;
    if (type === 'circle' || type === 'box') {
      if (type === 'circle') {
        collider = new Circle_Collider(w, h, ox, oy);
      } else {
        collider = new Box_Collider(w, h, ox, oy);
      }
    } else if (type === 'poly') {
      collider = new Polygon_Collider(arr.slice(1));
    } else {
      return null;
    }
    return collider;
  };

  ColliderManager.rayCast = function(origin, angle, dist, filter) {
    // Incomplete
    // need to finish the Polygon_Collider.prototype.lineIntersection function
    var ray = new Box_Collider(dist, 1, 0, 0, {
      pivot: new Point(0, 0.5),
      position: origin
    });
    //this.draw(ray, 600);
    return this.getAllNear(ray, filter);
  };
})();

//-----------------------------------------------------------------------------
// Game_Interpreter

(function() {
  var Alias_Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
  Game_Interpreter.prototype.pluginCommand = function(command, args) {
    if (command.toLowerCase() === 'qmovement') {
      return this.qMovementCommand(QPlus.makeArgs(args));
    }
    Alias_Game_Interpreter_pluginCommand.call(this, command, args);
  };

  Game_Interpreter.prototype.qMovementCommand = function(args) {
    var cmd = args.shift().toLowerCase();
    if (cmd === 'changecollider') {
      var chara = QPlus.getCharacter(args[0]);
      if (!chara) return;
      var type = args[1];
      var data = args.slice(2).map(QPlus.stringToType);
      chara.changeCollider(type, data);
      return;
    }
    if (cmd === 'transfer') {
      var mapId = Number(args[0]);
      var x = Number(args[1]) / QMovement.tileSize;
      var y = Number(args[2]) / QMovement.tileSize;
      var dir = Number(QPlus.getArg(args, /^dir(\d+)$/i)) || 0;
      var fade = QPlus.getArg(args, /fade(black|white)/i) || 'none';
      if (fade.toLowerCase() === 'black') {
        fade = 0;
      } else if (fade.toLowerCase() === 'white') {
        fade = 1;
      } else {
        fade = 3;
      }
      $gamePlayer.reserveTransfer(mapId, x, y, dir, fade);
      return;
    }
    if (cmd === 'setpos') {
      var chara;
      if (args[0].toLowerCase() === 'this') {
        chara = this.character(0);
      } else {
        chara = QPlus.getCharacter(args[0]);
      }
      if (!chara) return;
      var x = Number(args[1]) / QMovement.tileSize;
      var y = Number(args[2]) / QMovement.tileSize;
      var dir = Number(QPlus.getArg(args, /^dir(\d+)$/i)) || 0;
      chara.locate(x, y);
      if (dir > 0) {
        chara.setDirection(dir);
      }
      return;
    }
  };
})();

//-----------------------------------------------------------------------------
// Game_Temp
//
// The game object class for temporary data that is not included in save data.

(function() {
  var Alias_Game_Temp_initialize = Game_Temp.prototype.initialize;
  Game_Temp.prototype.initialize = function() {
    Alias_Game_Temp_initialize.call(this);
    this._destinationPX = null;
    this._destinationPY = null;
  };

  Game_Temp.prototype.setPixelDestination = function(x, y) {
    this._destinationPX = x;
    this._destinationPY = y;
    var x1 = $gameMap.roundX(Math.floor(x / $gameMap.tileWidth()));
    var y1 = $gameMap.roundY(Math.floor(y / $gameMap.tileHeight()));
    this.setDestination(x1, y1);
  };

  var Alias_Game_Temp_clearDestination = Game_Temp.prototype.clearDestination;
  Game_Temp.prototype.clearDestination = function() {
    if ($gamePlayer._movingWithMouse) return;
    Alias_Game_Temp_clearDestination.call(this);
    this._destinationPX = null;
    this._destinationPY = null;
  };

  Game_Temp.prototype.destinationPX = function() {
    return this._destinationPX;
  };

  Game_Temp.prototype.destinationPY = function() {
    return this._destinationPY;
  };
})();

//-----------------------------------------------------------------------------
// Game_System

(function() {
  var Alias_Game_System_onBeforeSave = Game_System.prototype.onBeforeSave;
  Game_System.prototype.onBeforeSave = function() {
    Alias_Game_System_onBeforeSave.call(this);
    $gameMap.clearColliders();
    ColliderManager._needsRefresh = true;
  };

  var Alias_Game_System_onAfterLoad = Game_System.prototype.onAfterLoad;
  Game_System.prototype.onAfterLoad = function() {
    Alias_Game_System_onAfterLoad.call(this);
    ColliderManager._needsRefresh = true;
  };
})();

//-----------------------------------------------------------------------------
// Game_Map

(function() {
  var Alias_Game_Map_setup = Game_Map.prototype.setup;
  Game_Map.prototype.setup = function(mapId) {
    if ($dataMap) {
      ColliderManager._mapWidth = this.width();
      ColliderManager._mapHeight = this.height();
      ColliderManager.refresh();
    }
    Alias_Game_Map_setup.call(this, mapId);
    this.reloadColliders();
  };

  Game_Map.prototype.tileWidth = function() {
    return QMovement.tileSize;
  };

  Game_Map.prototype.tileHeight = function() {
    return QMovement.tileSize;
  };

  Game_Map.prototype.flagAt = function(x, y) {
    var x = x || $gamePlayer.x;
    var y = y || $gamePlayer.y;
    var flags = this.tilesetFlags();
    var tiles = this.allTiles(x, y);
    for (var i = 0; i < tiles.length; i++) {
      var flag = flags[tiles[i]];
      console.log('layer', i, ':', flag);
      if (flag & 0x20) console.log('layer', i, 'is ladder');
      if (flag & 0x40) console.log('layer', i, 'is bush');
      if (flag & 0x80) console.log('layer', i, 'is counter');
      if (flag & 0x100) console.log('layer', i, 'is damage');
    }
  };

  Game_Map.prototype.gridSize = function() {
    if ($dataMap && $dataMap.meta.grid !== undefined) {
      return Number($dataMap.meta.grid) || QMovement.grid;
    }
    return QMovement.grid;
  };

  Game_Map.prototype.offGrid = function() {
    if ($dataMap && $dataMap.meta.offGrid !== undefined) {
      return $dataMap.meta.offGrid === 'true';
    }
    return QMovement.offGrid;
  };

  Game_Map.prototype.midPass = function() {
    if ($dataMap && $dataMap.meta.midPass !== undefined) {
      return $dataMap.meta.midPass === 'true';
    }
    return QMovement.midPass;
  };

  var Alias_Game_Map_refreshIfNeeded = Game_Map.prototype.refreshIfNeeded;
  Game_Map.prototype.refreshIfNeeded = function() {
    Alias_Game_Map_refreshIfNeeded.call(this);
    if (ColliderManager._needsRefresh) {
      ColliderManager._mapWidth = this.width();
      ColliderManager._mapHeight = this.height();
      ColliderManager.refresh();
      this.reloadColliders();
    }
  };

  Game_Map.prototype.reloadColliders = function() {
    this.reloadTileMap();
    var events = this.events();
    var i, j;
    for (i = 0, j = events.length; i < j; i++) {
      events[i].reloadColliders();
    }
    var vehicles = this._vehicles;
    for (i = 0, j = vehicles.length; i < j; i++) {
      vehicles[i].reloadColliders();
    }
    $gamePlayer.reloadColliders();
    var followers = $gamePlayer.followers()._data;
    for (i = 0, j = followers.length; i < j; i++) {
      followers[i].reloadColliders();
    }
  };

  Game_Map.prototype.clearColliders = function() {
    var events = this.events();
    var i, j;
    for (i = 0, j = events.length; i < j; i++) {
      events[i].removeColliders();
    }
    var vehicles = this._vehicles;
    for (i = 0, j = vehicles.length; i < j; i++) {
      vehicles[i].removeColliders();
    }
    $gamePlayer.removeColliders();
    var followers = $gamePlayer.followers()._data;
    for (i = 0, j = followers.length; i < j; i++) {
      followers[i].removeColliders();
    }
  };

  Game_Map.prototype.reloadTileMap = function() {
    this.setupMapColliders();
    // collider map is also loaded here
    // collision map is also loaded here
  };

  Game_Map.prototype.setupMapColliders = function() {
    this._tileCounter = 0;
    for (var x = 0; x < this.width(); x++) {
      for (var y = 0; y < this.height(); y++) {
        var flags = this.tilesetFlags();
        var tiles = this.allTiles(x, y);
        var id = x + y * this.width();
        for (var i = tiles.length - 1; i >= 0; i--) {
          var flag = flags[tiles[i]];
          if (flag === 16) continue;
          var data = this.getMapCollider(x, y, flag);
          if (!data) continue;
          if (data[0].constructor === Array) {
            for (var j = 0; j < data.length; j++) {
              this.makeTileCollider(x, y, flag, data[j], j);
            }
          } else {
            this.makeTileCollider(x, y, flag, data, 0);
          }
        }
      }
    }
  };

  Game_Map.prototype.getMapCollider = function(x, y, flag) {
    var realFlag = flag;
    if (flag >> 12 > 0) {
      flag = flag.toString(2);
      flag = flag.slice(flag.length - 12, flag.length);
      flag = parseInt(flag, 2);
    }
    var boxData;
    if (QMovement.regionColliders[this.regionId(x, y)]) {
      var regionData = QMovement.regionColliders[this.regionId(x, y)];
      boxData = [];
      for (var i = 0; i < regionData.length; i++) {
        boxData[i] = [
          regionData[i].width || 0,
          regionData[i].height || 0,
          regionData[i].ox || 0,
          regionData[i].oy || 0,
          regionData[i].tag || regionData[i].note || '',
          regionData[i].type || 'box'
        ]
      }
      flag = 0;
    } else {
      boxData = QMovement.tileBoxes[flag];
    }
    if (!boxData) {
      if (flag & 0x20 || flag & 0x40 || flag & 0x80 || flag & 0x100) {
        boxData = [this.tileWidth(), this.tileHeight(), 0, 0];
      } else {
        return null;
      }
    }
    return boxData;
  };

  Game_Map.prototype.makeTileCollider = function(x, y, flag, boxData, index) {
    // boxData is array [width, height, ox, oy, note, type]
    var x1 = x * this.tileWidth();
    var y1 = y * this.tileHeight();
    var ox = boxData[2] || 0;
    var oy = boxData[3] || 0;
    var w = boxData[0];
    var h = boxData[1];
    if (w === 0 || h === 0) return;
    var type = boxData[5] || 'box';
    var newBox;
    if (type === 'circle') {
      newBox = new Circle_Collider(w, h, ox, oy);
    } else if (type === 'box') {
      newBox = new Box_Collider(w, h, ox, oy);
    } else {
      return;
    }
    newBox.isTile = true;
    newBox.note = boxData[4] || '';
    newBox.flag = flag;
    newBox.terrain = flag >> 12;
    newBox.regionId = this.regionId(x, y);
    newBox.isWater1 = flag >> 12 === QMovement.water1Tag || /<water1>/i.test(newBox.note);
    newBox.isWater2 = flag >> 12 === QMovement.water2Tag || /<water2>/i.test(newBox.note);
    newBox.isLadder = (flag & 0x20) || /<ladder>/i.test(newBox.note);
    newBox.isBush = (flag & 0x40) || /<bush>/i.test(newBox.note);
    newBox.isCounter = (flag & 0x80) || /<counter>/i.test(newBox.note);
    newBox.isDamage = (flag & 0x100) || /<damage>/i.test(newBox.note);
    newBox.moveTo(x1, y1);
    var vx = x * this.height() * this.width();
    var vy = y * this.height();
    var vz = index;
    newBox.location = vx + vy + vz;
    if (newBox.isWater2) {
      newBox.color = QMovement.water2.toLowerCase();
    } else if (newBox.isWater1) {
      newBox.color = QMovement.water1.toLowerCase();
    } else if (newBox.isLadder || newBox.isBush || newBox.isDamage) {
      newBox.color = '#ffffff';
    } else {
      newBox.color = QMovement.collision.toLowerCase();
    }
    ColliderManager.addCollider(newBox, -1);
    return newBox;
  };

  Game_Map.prototype.adjustPX = function(x) {
    return this.adjustX(x / QMovement.tileSize) * QMovement.tileSize;
  };

  Game_Map.prototype.adjustPY = function(y) {
    return this.adjustY(y / QMovement.tileSize) * QMovement.tileSize;
  };

  Game_Map.prototype.roundPX = function(x) {
    return this.isLoopHorizontal() ? x.mod(this.width() * QMovement.tileSize) : x;
  };

  Game_Map.prototype.roundPY = function(y) {
    return this.isLoopVertical() ? y.mod(this.height() * QMovement.tileSize) : y;
  };

  Game_Map.prototype.pxWithDirection = function(x, d, dist) {
    return x + (d === 6 ? dist : d === 4 ? -dist : 0);
  };

  Game_Map.prototype.pyWithDirection = function(y, d, dist) {
    return y + (d === 2 ? dist : d === 8 ? -dist : 0);
  };

  Game_Map.prototype.roundPXWithDirection = function(x, d, dist) {
    return this.roundPX(x + (d === 6 ? dist : d === 4 ? -dist : 0));
  };

  Game_Map.prototype.roundPYWithDirection = function(y, d, dist) {
    return this.roundPY(y + (d === 2 ? dist : d === 8 ? -dist : 0));
  };

  Game_Map.prototype.deltaPX = function(x1, x2) {
    var result = x1 - x2;
    if (this.isLoopHorizontal() && Math.abs(result) > (this.width() * QMovement.tileSize) / 2) {
      if (result < 0) {
        result += this.width() * QMovement.tileSize;
      } else {
        result -= this.width() * QMovement.tileSize;
      }
    }
    return result;
  };

  Game_Map.prototype.deltaPY = function(y1, y2) {
    var result = y1 - y2;
    if (this.isLoopVertical() && Math.abs(result) > (this.height() * QMovement.tileSize) / 2) {
      if (result < 0) {
        result += this.height() * QMovement.tileSize;
      } else {
        result -= this.height() * QMovement.tileSize;
      }
    }
    return result;
  };

  Game_Map.prototype.canvasToMapPX = function(x) {
    var tileWidth = this.tileWidth();
    var originX = this.displayX() * tileWidth;
    return this.roundPX(originX + x);
  };

  Game_Map.prototype.canvasToMapPY = function(y) {
    var tileHeight = this.tileHeight();
    var originY = this.displayY() * tileHeight;
    return this.roundPY(originY + y);
  };
})();

//-----------------------------------------------------------------------------
// Game_Party

(function() {
  Game_Party.prototype.steps = function() {
    return Math.floor(this._steps);
  };

  Game_Party.prototype.increaseSteps = function() {
    this._steps += $gamePlayer.moveTiles() / QMovement.tileSize;
  };
})();

//-----------------------------------------------------------------------------
// Game_CharacterBase

(function() {
  Object.defineProperties(Game_CharacterBase.prototype, {
    px: {
      get: function() { return this._px; },
      configurable: true
    },
    py: {
      get: function() { return this._py; },
      configurable: true
    }
  });

  var Alias_Game_CharacterBase_initMembers = Game_CharacterBase.prototype.initMembers;
  Game_CharacterBase.prototype.initMembers = function() {
    Alias_Game_CharacterBase_initMembers.call(this);
    this._px = 0;
    this._py = 0;
    this._realPX = 0;
    this._realPY = 0;
    this._radian = this.directionToRadian(this._direction);
    this._forwardRadian = this.directionToRadian(this._direction);
    this._adjustFrameSpeed = false;
    this._freqCount = 0;
    this._diagonal = false;
    this._currentRad = 0;
    this._targetRad = 0;
    this._pivotX = 0;
    this._pivotY = 0;
    this._radiusL = 0;
    this._radisuH = 0;
    this._angularSpeed;
    this._passabilityLevel = 0; // TODO
    this._isMoving = false;
    this._smartMove = 0;
    this._colliders = null;
    this._overrideColliders = {};
  };

  Game_CharacterBase.prototype.direction8 = function(horz, vert) {
    if (horz === 4 && vert === 8) return 7;
    if (horz === 4 && vert === 2) return 1;
    if (horz === 6 && vert === 8) return 9;
    if (horz === 6 && vert === 2) return 3;
    return 5;
  };

  Game_CharacterBase.prototype.isMoving = function() {
    return this._isMoving;
  };

  Game_CharacterBase.prototype.startedMoving = function() {
    return this._realPX !== this._px || this._realPY !== this._py;
  };

  Game_CharacterBase.prototype.isDiagonal = function() {
    return this._diagonal;
  };

  Game_CharacterBase.prototype.isArcing = function() {
    return this._currentRad !== this._targetRad;
  };

  Game_CharacterBase.prototype.setPixelPosition = function(x, y) {
    this.setPosition(x / QMovement.tileSize, y / QMovement.tileSize);
  };

  var Alias_Game_CharacterBase_setPosition = Game_CharacterBase.prototype.setPosition;
  Game_CharacterBase.prototype.setPosition = function(x, y) {
    Alias_Game_CharacterBase_setPosition.call(this, x, y);
    this._px = this._realPX = x * QMovement.tileSize;
    this._py = this._realPY = y * QMovement.tileSize;
    if (!this._colliders) this.collider();
    this.moveColliders();
  };

  var Alias_Game_CharacterBase_copyPosition = Game_CharacterBase.prototype.copyPosition;
  Game_CharacterBase.prototype.copyPosition = function(character) {
    Alias_Game_CharacterBase_copyPosition.call(this, character);
    this._px = character._px;
    this._py = character._py;
    this._realPX = character._realPX;
    this._realPY = character._realPY;
    if (!this._colliders) this.collider();
    this.moveColliders();
  };

  var Alias_Game_CharacterBase_setDirection = Game_CharacterBase.prototype.setDirection;
  Game_CharacterBase.prototype.setDirection = function(d) {
    if (d) this._radian = this.directionToRadian(d);
    if (!this.isDirectionFixed() && d) {
      if ([1, 3, 7, 9].contains(d)) {
        this._diagonal = d;
        var horz = [1, 7].contains(d) ? 4 : 6;
        var vert = [1, 3].contains(d) ? 2 : 8;
        if (this._direction === this.reverseDir(horz)) {
          this._direction = horz;
        }
        if (this._direction === this.reverseDir(vert)) {
          this._direction = vert;
        }
        this.resetStopCount();
        return;
      } else {
        this._diagonal = false;
      }
    }
    Alias_Game_CharacterBase_setDirection.call(this, d);
  };

  Game_CharacterBase.prototype.setRadian = function(radian) {
    radian = QPlus.adjustRadian(radian);
    this.setDirection(this.radianToDirection(radian, QMovement.diagonal));
    this._radian = radian;
  };

  Game_CharacterBase.prototype.moveTiles = function() {
    if ($gameMap.gridSize() < this.frameSpeed()) {
      return $gameMap.offGrid() ? this.frameSpeed() : $gameMap.gridSize();
    }
    return $gameMap.gridSize();
  };

  Game_CharacterBase.prototype.frameSpeed = function(multi) {
    var multi = multi === undefined ? 1 : Math.abs(multi);
    return this.distancePerFrame() * QMovement.tileSize * multi;
  };

  Game_CharacterBase.prototype.angularSpeed = function() {
    return this._angularSpeed || this.frameSpeed() / this._radiusL;
  };

  Game_CharacterBase.prototype.forwardV = function() {
    return {
      x: Math.cos(this._forwardRadian) * this.frameSpeed(),
      y: Math.sin(this._forwardRadian) * this.frameSpeed()
    }
  };

  var Alias_Game_CharacterBase_canMove = Game_CharacterBase.prototype.canMove;
  Game_CharacterBase.prototype.canMove = function() {
    if (this._locked) return false;
    return Alias_Game_CharacterBase_canMove.call(this);
  };

  Game_CharacterBase.prototype.canPass = function(x, y, dir) {
    return this.canPixelPass(x * QMovement.tileSize, y * QMovement.tileSize, dir);
  };

  Game_CharacterBase.prototype.canPixelPass = function(x, y, dir, dist, type) {
    dist = dist || this.moveTiles();
    type = type || 'collision';
    var x1 = $gameMap.roundPXWithDirection(x, dir, dist);
    var y1 = $gameMap.roundPYWithDirection(y, dir, dist);
    if (!this.collisionCheck(x1, y1, dir, dist, type)) {
      this.collider(type).moveTo(this._px, this._py);
      return false;
    }
    if (type[0] !== '_') {
      this.moveColliders(x1, y1);
    }
    return true;
  };

  Game_CharacterBase.prototype.canPassDiagonally = function(x, y, horz, vert) {
    return this.canPixelPassDiagonally(x * QMovement.tileSize, y * QMovement.tileSize, horz, vert);
  };

  Game_CharacterBase.prototype.canPixelPassDiagonally = function(x, y, horz, vert, dist, type) {
    dist = dist || this.moveTiles();
    type = type || 'collision';
    var x1 = $gameMap.roundPXWithDirection(x, horz, dist);
    var y1 = $gameMap.roundPYWithDirection(y, vert, dist);
    if (dist === this.moveTiles()) {
      if (!this.canPixelPass(x1, y1, 5, null, type)) return false;
      if ($gameMap.midPass()) {
        var x2 = $gameMap.roundPXWithDirection(x, horz, dist / 2);
        var y2 = $gameMap.roundPYWithDirection(y, vert, dist / 2);
        if (!this.canPixelPass(x2, y2, 5, null, type)) return false;
      }
    } else {
      return (this.canPixelPass(x, y, vert, dist, type) && this.canPixelPass(x, y1, horz, dist, type)) &&
        (this.canPixelPass(x, y, horz, dist, type) && this.canPixelPass(x1, y, vert, dist, type));
    }
    return true;
  };

  Game_CharacterBase.prototype.collisionCheck = function(x, y, dir, dist, type) {
    this.collider(type).moveTo(x, y);
    if (!this.valid(type)) return false;
    if (this.isThrough() || this.isDebugThrough()) return true;
    if ($gameMap.midPass() && dir !== 5) {
      if (!this.middlePass(x, y, dir, dist, type)) return false;
    }
    if (this.collidesWithAnyTile(type)) return false;
    if (this.collidesWithAnyCharacter(type)) return false;
    return true;
  };

  Game_CharacterBase.prototype.middlePass = function(x, y, dir, dist, type) {
    var dist = dist / 2 || this.moveTiles() / 2;
    var x2 = $gameMap.roundPXWithDirection(x, this.reverseDir(dir), dist);
    var y2 = $gameMap.roundPYWithDirection(y, this.reverseDir(dir), dist);
    this.collider(type).moveTo(x2, y2);
    if (this.collidesWithAnyTile(type)) return false;
    if (this.collidesWithAnyCharacter(type)) return false;
    this.collider(type).moveTo(x, y);
    return true;
  };

  Game_CharacterBase.prototype.collidesWithAnyTile = function(type) {
    var collider = this.collider(type);
    var collided = false;
    ColliderManager.getCollidersNear(collider, (function(collider) {
      collided = this.collidedWithTile(type, collider);
      if (collided) return 'break';
    }).bind(this));
    return collided;
  };

  Game_CharacterBase.prototype.collidedWithTile = function(type, collider) {
    if (collider.color && this.passableColors().contains(collider.color)) {
      return false;
    }
    if (collider.type && (collider.type !== 'collision' || collider.type !== 'default')) {
      return false;
    }
    return collider.intersects(this.collider(type));
  };

  Game_CharacterBase.prototype.collidesWithAnyCharacter = function(type) {
    var collider = this.collider(type);
    var collided = false;
    ColliderManager.getCharactersNear(collider, function(chara) {
      collided = this.collidedWithCharacter(type, chara);
      if (collided) return 'break';
    }.bind(this));
    return collided;
  };

  Game_CharacterBase.prototype.collidedWithCharacter = function(type, chara) {
    if (chara.isThrough() || chara === this || !chara.isNormalPriority()) {
      return false;
    }
    if (this.ignoreCharacters(type).contains(chara.charaId())) {
      return false;
    }
    return chara.collider('collision').intersects(this.collider(type));
  };

  Game_CharacterBase.prototype.ignoreCharacters = function(type) {
    // This function is to be aliased by plugins to return a list
    // of charaId's this character can pass through
    return [];
  };

  Game_CharacterBase.prototype.valid = function(type) {
    var edge = this.collider(type).gridEdge();
    var maxW = $gameMap.width();
    var maxH = $gameMap.height();
    if (!$gameMap.isLoopHorizontal()) {
      if (edge.x1 < 0 || edge.x2 >= maxW) return false;
    }
    if (!$gameMap.isLoopVertical()) {
      if (edge.y1 < 0 || edge.y2 >= maxH) return false;
    }
    return true;
  };

  Game_CharacterBase.prototype.passableColors = function() {
    // #00000000 is a transparent return value in collisionmap addon
    var colors = ['#ffffff', '#00000000'];
    switch (this._passabilityLevel) {
      case 1:
      case 3: {
        colors.push(QMovement.water1);
        break;
      }
      case 2:
      case 4: {
        colors.push(QMovement.water1);
        colors.push(QMovement.water2);
        break;
      }
    }
    return colors;
  };

  Game_CharacterBase.prototype.canPassToFrom = function(xf, yf, xi, yi, type) {
    xi = xi === undefined ? this._px : xi;
    yi = yi === undefined ? this._py : yi;
    type = type || 'collision';
    // TODO remove this check by having the start and end colliders
    // be included in the _stretched collider
    if (!this.canPixelPass(xi, yi, 5, null, type) || !this.canPixelPass(xf, yf, 5, null, type)) {
      this.collider(type).moveTo(this._px, this._py);
      return false;
    }
    var dx = xf - xi;
    var dy = yf - yi;
    var radian = Math.atan2(dy, dx);
    if (radian < 0) radian += Math.PI * 2;
    var dist = Math.sqrt(dx * dx + dy * dy);
    this._colliders['_stretched'] = this.collider(type).stretchedPoly(radian, dist);
    if (!this.canPixelPass(xi, yi, 5, null, '_stretched')) {
      delete this._colliders['_stretched'];
      return false;
    }
    delete this._colliders['_stretched'];
    return true;
  };

  Game_CharacterBase.prototype.checkEventTriggerTouchFront = function(d) {
    var horz = vert = d;
    if ([1, 3, 7, 9].contains(d)) {
      horz = (d === 1 || d === 7) ? 4 : 6;
      vert = (d === 1 || d === 3) ? 2 : 8;
    }
    var x2 = $gameMap.roundPXWithDirection(this.px, horz, this.moveTiles());
    var y2 = $gameMap.roundPYWithDirection(this.py, vert, this.moveTiles());
    this.checkEventTriggerTouch(x2, y2);
  };

  Game_CharacterBase.prototype.isOnLadder = function() {
    if (!this.collider()) return false;
    var collider = this.collider('collision');
    var collided = false;
    var colliders = ColliderManager.getCollidersNear(collider, function(tile) {
      if (!tile.isTile) return false;
      if (tile.isLadder && tile.intersects(collider)) {
        collided = true;
        return 'break';
      }
      return false;
    });
    return collided;
  };

  Game_CharacterBase.prototype.isOnBush = function() {
    if (!this.collider()) return false;
    var collider = this.collider('collision');
    var collided = false;
    var colliders = ColliderManager.getCollidersNear(collider, function(tile) {
      if (!tile.isTile) return false;
      if (tile.isBush && tile.intersects(collider)) {
        collided = true;
        return 'break';
      }
      return false;
    });
    return collided;
  };

  Game_CharacterBase.prototype.freqThreshold = function() {
    return QMovement.tileSize;
  };

  Game_CharacterBase.prototype.terrainTag = function() {
    return $gameMap.terrainTag(Math.floor(this.cx(true)), Math.floor(this.cy(true)));
  };

  Game_CharacterBase.prototype.regionId = function() {
    return $gameMap.regionId(Math.floor(this.cx(true)), Math.floor(this.cy(true)));
  };

  var Alias_Game_CharacterBase_update = Game_CharacterBase.prototype.update;
  Game_CharacterBase.prototype.update = function() {
    var prevX = this._realPX;
    var prevY = this._realPY;
    if (this.startedMoving()) {
      this._isMoving = true;
    } else {
      this.updateStop();
    }
    if (this.isArcing()) {
      this.updateArc();
    } else if (this.isJumping()) {
      this.updateJump();
    } else if (this.isMoving()) {
      this.updateMove();
    }
    this.updateAnimation();
    this.updateColliders();
    if (prevX !== this._realPX || prevY !== this._realPY) {
      this.onPositionChange();
    } else {
      this._isMoving = false;
    }
  };

  Game_CharacterBase.prototype.updateMove = function() {
    var xSpeed = 1;
    var ySpeed = 1;
    if (this._adjustFrameSpeed) {
      xSpeed = Math.cos(this._radian);
      ySpeed = Math.sin(this._radian);
    }
    if (this._px < this._realPX) {
      this._realPX = Math.max(this._realPX - this.frameSpeed(xSpeed), this._px);
    }
    if (this._px > this._realPX) {
      this._realPX = Math.min(this._realPX + this.frameSpeed(xSpeed), this._px);
    }
    if (this._py < this._realPY) {
      this._realPY = Math.max(this._realPY - this.frameSpeed(ySpeed), this._py);
    }
    if (this._py > this._realPY) {
      this._realPY = Math.min(this._realPY + this.frameSpeed(ySpeed), this._py);
    }
    this._x = this._realX = this._realPX / QMovement.tileSize;
    this._y = this._realY = this._realPY / QMovement.tileSize;
    this._freqCount += this.frameSpeed();
  };

  Game_CharacterBase.prototype.updateArc = function() {
    if (this._currentRad < this._targetRad) {
      var newRad = Math.min(this._currentRad + this.angularSpeed(), this._targetRad);
    }
    if (this._currentRad > this._targetRad) {
      var newRad = Math.max(this._currentRad - this.angularSpeed(), this._targetRad);
    }
    var x1 = this._pivotX + this._radiusL * Math.cos(newRad);
    var y1 = this._pivotY + this._radiusH * Math.sin(newRad);
    this._currentRad = newRad;
    this._px = this._realPX = x1;
    this._py = this._realPY = y1;
    this._x = this._realX = this._realPX / QMovement.tileSize;
    this._y = this._realY = this._realPY / QMovement.tileSize;
    this.moveColliders(x1, y1);
    this.checkEventTriggerTouchFront(this._direction);
  };

  var Alias_Game_CharacterBase_updateJump = Game_CharacterBase.prototype.updateJump;
  Game_CharacterBase.prototype.updateJump = function() {
    Alias_Game_CharacterBase_updateJump.call(this);
    this._px = this._realPX = this._x * QMovement.tileSize;
    this._py = this._realPY = this._y * QMovement.tileSize;
    this.moveColliders(this._px, this._py);
  };

  Game_CharacterBase.prototype.updateColliders = function() {
    var colliders = this._colliders;
    if (!colliders) return;
    var hidden = false;
    hidden = this.isTransparent() || this._erased;
    if (!hidden && this.isVisible) {
      hidden = !this.isVisible();
    }
    for (var type in colliders) {
      if (colliders.hasOwnProperty(type)) {
        colliders[type]._isHidden = !!hidden;
      }
    }
  };

  Game_CharacterBase.prototype.onPositionChange = function() {
    this.refreshBushDepth();
  };

  Game_CharacterBase.prototype.refreshBushDepth = function() {
    if (this.isNormalPriority() && !this.isObjectCharacter() &&
      this.isOnBush() && !this.isJumping()) {
      if (!this.startedMoving()) this._bushDepth = 12;
    } else {
      this._bushDepth = 0;
    }
  };

  Game_CharacterBase.prototype.pixelJump = function(xPlus, yPlus) {
    return this.jump(xPlus / QMovement.tileSize, yPlus / QMovement.tileSize);
  };

  Game_CharacterBase.prototype.pixelJumpForward = function(dist, dir) {
    dir = dir || this._direction;
    dist = dist / QMovement.tileSize;
    var x = dir === 6 ? dist : dir === 4 ? -dist : 0;
    var y = dir === 2 ? dist : dir === 8 ? -dist : 0;
    this.jump(x, y);
  };

  Game_CharacterBase.prototype.pixelJumpBackward = function(dist) {
    this.pixelJumpFixed(this.reverseDir(this.direction()), dist);
  };

  Game_CharacterBase.prototype.pixelJumpFixed = function(dir, dist) {
    var lastDirectionFix = this.isDirectionFixed();
    this.setDirectionFix(true);
    this.pixelJumpForward(dist, dir);
    this.setDirectionFix(lastDirectionFix);
  };

  Game_CharacterBase.prototype.moveStraight = function(dir, dist) {
    dist = dist || this.moveTiles();
    this.setMovementSuccess(this.canPixelPass(this._px, this._py, dir, dist));
    var originalSpeed = this._moveSpeed;
    if (this.smartMove() === 1 || this.smartMove() > 2) {
      this.smartMoveSpeed(dir);
    }
    this.setDirection(dir);
    if (this.isMovementSucceeded()) {
      this._forwardRadian = this.directionToRadian(dir);
      this._diagonal = false;
      this._adjustFrameSpeed = false;
      this._px = $gameMap.roundPXWithDirection(this._px, dir, dist);
      this._py = $gameMap.roundPYWithDirection(this._py, dir, dist);
      this._realPX = $gameMap.pxWithDirection(this._px, this.reverseDir(dir), dist);
      this._realPY = $gameMap.pyWithDirection(this._py, this.reverseDir(dir), dist);
      this.increaseSteps();
    } else {
      this.checkEventTriggerTouchFront(dir);
    }
    this._moveSpeed = originalSpeed;
    if (!this.isMovementSucceeded() && this.smartMove() > 1) {
      this.smartMoveDir8(dir);
    }
  };

  Game_CharacterBase.prototype.moveDiagonally = function(horz, vert, dist) {
    dist = dist || this.moveTiles();
    this.setMovementSuccess(this.canPixelPassDiagonally(this._px, this._py, horz, vert, dist));
    var originalSpeed = this._moveSpeed;
    if (this.smartMove() === 1 || this.smartMove() > 2) {
      this.smartMoveSpeed([horz, vert]);
    }
    this.setDirection(this.direction8(horz, vert));
    if (this.isMovementSucceeded()) {
      this._forwardRadian = this.directionToRadian(this.direction8(horz, vert));
      this._adjustFrameSpeed = false;
      this._px = $gameMap.roundPXWithDirection(this._px, horz, dist);
      this._py = $gameMap.roundPYWithDirection(this._py, vert, dist);
      this._realPX = $gameMap.pxWithDirection(this._px, this.reverseDir(horz), dist);
      this._realPY = $gameMap.pyWithDirection(this._py, this.reverseDir(vert), dist);
      this.increaseSteps();
    } else {
      this.checkEventTriggerTouchFront(this.direction8(horz, vert));
    }
    this._moveSpeed = originalSpeed;
    if (!this.isMovementSucceeded() && this.smartMove() > 1) {
      if (this.canPixelPass(this._px, this._py, horz)) {
        this.moveStraight(horz);
      } else if (this.canPixelPass(this._px, this._py, vert)) {
        this.moveStraight(vert);
      }
    }
  };

  Game_CharacterBase.prototype.moveRadian = function(radian, dist) {
    dist = dist || this.moveTiles();
    this.fixedRadianMove(radian, dist);
    if (!this.isMovementSucceeded() && this.smartMove() > 1) {
      var realDir = this.radianToDirection(radian, true);
      var xAxis = Math.cos(radian);
      var yAxis = Math.sin(radian);
      var horz = xAxis > 0 ? 6 : xAxis < 0 ? 4 : 0;
      var vert = yAxis > 0 ? 2 : yAxis < 0 ? 8 : 0;
      if ([1, 3, 7, 9].contains(realDir)) {
        if (this.canPixelPass(this._px, this._py, horz, dist)) {
          this.moveStraight(horz, dist);
        } else if (this.canPixelPass(this._px, this._py, vert, dist)) {
          this.moveStraight(vert, dist);
        }
      } else {
        var dir = this.radianToDirection(radian);
        this.smartMoveDir8(dir);
      }
    }
  };

  Game_CharacterBase.prototype.fixedMove = function(dir, dist) {
    dist = dist || this.moveTiles();
    dir = dir === 5 ? this.direction() : dir;
    if ([1, 3, 7, 9].contains(dir)) {
      var horz = (dir === 1 || dir === 7) ? 4 : 6;
      var vert = (dir === 1 || dir === 3) ? 2 : 8;
      return this.fixedDiagMove(horz, vert, dist);
    }
    this.setMovementSuccess(this.canPixelPass(this._px, this._py, dir, dist));
    this.setDirection(dir);
    if (this.isMovementSucceeded()) {
      this._forwardRadian = this.directionToRadian(dir);
      this._adjustFrameSpeed = false;
      this._px = $gameMap.roundPXWithDirection(this._px, dir, dist);
      this._py = $gameMap.roundPYWithDirection(this._py, dir, dist);
      this._realPX = $gameMap.pxWithDirection(this._px, this.reverseDir(dir), dist);
      this._realPY = $gameMap.pyWithDirection(this._py, this.reverseDir(dir), dist);
      this.increaseSteps();
    } else {
      this.checkEventTriggerTouchFront(dir);
    }
  };

  Game_CharacterBase.prototype.fixedDiagMove = function(horz, vert, dist) {
    dist = dist || this.moveTiles();
    this.setMovementSuccess(this.canPixelPassDiagonally(this._px, this._py, horz, vert));
    this.setDirection(this.direction8(horz, vert));
    if (this.isMovementSucceeded()) {
      this._forwardRadian = this.directionToRadian(this.direction8(horz, vert));
      this._adjustFrameSpeed = false;
      this._px = $gameMap.roundPXWithDirection(this._px, horz, dist);
      this._py = $gameMap.roundPYWithDirection(this._py, vert, dist);
      this._realPX = $gameMap.pxWithDirection(this._px, this.reverseDir(horz), dist);
      this._realPY = $gameMap.pyWithDirection(this._py, this.reverseDir(vert), dist);
      this.increaseSteps();
    } else {
      this.checkEventTriggerTouchFront(this.direction8(horz, vert));
    }
  };

  Game_CharacterBase.prototype.fixedRadianMove = function(radian, dist) {
    dist = dist || this.moveTiles();
    var dir = this.radianToDirection(radian, true);
    var xAxis = Math.cos(radian);
    var yAxis = Math.sin(radian);
    var horzSteps = Math.abs(xAxis) * dist;
    var vertSteps = Math.abs(yAxis) * dist;
    var horz = xAxis > 0 ? 6 : xAxis < 0 ? 4 : 0;
    var vert = yAxis > 0 ? 2 : yAxis < 0 ? 8 : 0;
    var x2 = $gameMap.roundPXWithDirection(this._px, horz, horzSteps);
    var y2 = $gameMap.roundPYWithDirection(this._py, vert, vertSteps);
    this.setMovementSuccess(this.canPassToFrom(x2, y2, this._px, this._py));
    this.setRadian(radian);
    if (this.isMovementSucceeded()) {
      this._forwardRadian = QPlus.adjustRadian(radian);
      this._adjustFrameSpeed = true;
      this._px = x2;
      this._py = y2;
      this._realPX = $gameMap.pxWithDirection(this._px, this.reverseDir(horz), horzSteps);
      this._realPY = $gameMap.pyWithDirection(this._py, this.reverseDir(vert), vertSteps);
      this.increaseSteps();
    } else {
      this.checkEventTriggerTouchFront(dir);
    }
  };

  Game_CharacterBase.prototype.fixedMoveBackward = function(dist) {
    var lastDirectionFix = this.isDirectionFixed();
    this.setDirectionFix(true);
    this.fixedMove(this.reverseDir(this.direction()), dist);
    this.setDirectionFix(lastDirectionFix);
  };

  Game_CharacterBase.prototype.arc = function(pivotX, pivotY, radians, cc, frames) {
    var cc = cc ? 1 : -1;
    var dx = this._px - pivotX;
    var dy = this._py - pivotY;
    var rad = Math.atan2(dy, dx);
    frames = frames || 1;
    rad += rad < 0 ? 2 * Math.PI : 0;
    this._currentRad = rad;
    this._targetRad = rad + radians * cc;
    this._pivotX = pivotX;
    this._pivotY = pivotY;
    this._radiusL = this._radiusH = Math.sqrt(dy * dy + dx * dx);
    this._angularSpeed = radians / frames;
  };

  Game_CharacterBase.prototype.smartMove = function() {
    return this._smartMove;
  };

  Game_CharacterBase.prototype.smartMoveDir8 = function(dir) {
    var dist = this.moveTiles();
    var collider = this.collider('collision');
    var x1 = this._px;
    var y1 = this._py;
    var x2 = $gameMap.roundPXWithDirection(x1, dir, dist);
    var y2 = $gameMap.roundPYWithDirection(y1, dir, dist);
    collider.moveTo(x2, y2);
    var collided = false;
    ColliderManager.getCharactersNear(collider, (function(chara) {
      if (chara.isThrough() || chara === this || !chara.isNormalPriority() ||
        /<smartdir>/i.test(chara.notes())) {
        return false;
      }
      if (chara.collider('collision').intersects(collider)) {
        collided = true;
        return 'break';
      }
      return false;
    }).bind(this));
    collider.moveTo(x1, y1);
    if (collided) return;
    var horz = [4, 6].contains(dir) ? true : false;
    var steps = horz ? collider.height : collider.width;
    steps /= 2;
    var pass = false;
    for (var i = 0; i < 2; i++) {
      var sign = i === 0 ? 1 : -1;
      var j = 0;
      var x2 = x1;
      var y2 = y1;
      if (horz) {
        x2 = $gameMap.roundPXWithDirection(x1, dir, dist);
      } else {
        y2 = $gameMap.roundPYWithDirection(y1, dir, dist);
      }
      while (j < steps) {
        j += dist;
        if (horz) {
          y2 = y1 + j * sign;
        } else {
          x2 = x1 + j * sign;
        }
        pass = this.canPixelPass(x2, y2, 5);
        if (pass) break;
      }
      if (pass) break;
    }
    if (!pass) return;
    var radian = QPlus.adjustRadian(Math.atan2(y2 - y1, x2 - x1));
    this._forwardRadian = radian;
    this._px = x2;
    this._py = y2;
    this._realPX = x1;
    this._realPY = y1;
    this._adjustFrameSpeed = false;
    this.setRadian(radian);
    this.increaseSteps();
  };

  Game_CharacterBase.prototype.smartMoveSpeed = function(dir) {
    var diag = dir.constructor === Array;
    while (!this.isMovementSucceeded()) {
      // should improve by figuring out what 1 pixel is in terms of movespeed
      // and subtract by that value instead
      this._moveSpeed--;
      if (diag) {
        this.setMovementSuccess(this.canPixelPassDiagonally(this._px, this._py, dir[0], dir[1]));
      } else {
        this.setMovementSuccess(this.canPixelPass(this._px, this._py, dir));
      }
      if (this._moveSpeed < 1) break;
    }
  };

  Game_CharacterBase.prototype.reloadColliders = function() {
    this.removeColliders();
    this.setupColliders();
  };

  Game_CharacterBase.prototype.removeColliders = function() {
    ColliderManager.remove(this);
    for (var collider in this._colliders) {
      if (!this._colliders.hasOwnProperty(collider)) continue;
      ColliderManager.remove(this._colliders[collider]);
      this._colliders[collider] = null;
    }
    this._colliders = null;
  };

  // Can pass multiple types into args, ect:
  // collider('collision', 'interaction', 'default')
  // will return first one thats found
  Game_CharacterBase.prototype.collider = function(type, alternative) {
    if (!this._colliders) this.setupColliders();
    for (var i = 0; i < arguments.length; i++) {
      if (this._colliders[arguments[i]]) {
        return this._colliders[arguments[i]];
      }
    }
    return this._colliders['default'];
  };

  Game_CharacterBase.prototype.defaultColliderConfig = function() {
    return 'box,0,0';
  };

  Game_CharacterBase.prototype.setupColliders = function() {
    this._colliders = {};
    var defaultCollider = this.defaultColliderConfig();
    var notes = this.notes(true);
    var configs = {};
    var multi = /<colliders>([\s\S]*)<\/colliders>/i.exec(notes);
    var single = /<collider[:|=](.*?)>/i.exec(notes);
    if (multi) {
      configs = QPlus.stringToObj(multi[1]);
    }
    if (single) {
      configs.default = QPlus.stringToAry(single[1]);
    } else if (!configs.default) {
      configs.default = QPlus.stringToAry(defaultCollider);
    }
    Object.assign(configs, this._overrideColliders);
    for (var collider in configs) {
      this.makeCollider(collider, configs[collider]);
    }
    this.makeBounds();
    this.moveColliders();
  };

  Game_CharacterBase.prototype.makeCollider = function(type, settings) {
    this._colliders[type] = ColliderManager.convertToCollider(settings);
    this._colliders[type].oy -= this.shiftY();
    this._colliders[type]._charaId = this.charaId();
    ColliderManager.addCollider(this._colliders[type], -1, true);
  };

  Game_CharacterBase.prototype.changeCollider = function(type, settings) {
    this._overrideColliders[type] = settings;
    this.reloadColliders();
  };

  Game_CharacterBase.prototype.makeBounds = function() {
    var minX = null;
    var maxX = null;
    var minY = null;
    var maxY = null;
    for (var type in this._colliders) {
      if (!this._colliders.hasOwnProperty(type)) continue;
      var edge = this._colliders[type].edge();
      if (minX === null || minX > edge.x1) {
        minX = edge.x1;
      }
      if (maxX === null || maxX < edge.x2) {
        maxX = edge.x2;
      }
      if (minY === null || minY > edge.y1) {
        minY = edge.y1;
      }
      if (maxY === null || maxY < edge.y2) {
        maxY = edge.y2;
      }
    }
    var w = maxX - minX + 1;
    var h = maxY - minY + 1;
    this._colliders['bounds'] = new Box_Collider(w, h, minX, minY);
    this._colliders['bounds']._charaId = String(this.charaId());
    ColliderManager.addCharacter(this, 0);
  };

  Game_CharacterBase.prototype.moveColliders = function(x, y) {
    x = typeof x === 'number' ? x : this.px;
    y = typeof y === 'number' ? y : this.py;
    var prev = this._colliders['bounds'].sectorEdge();
    for (var collider in this._colliders) {
      if (this._colliders.hasOwnProperty(collider)) {
        this._colliders[collider].moveTo(x, y);
      }
    }
    ColliderManager.updateGrid(this, prev);
  };

  Game_CharacterBase.prototype.cx = function(grid) {
    var x = this.collider('collision').center.x;;
    if (grid) x /= QMovement.tileSize;
    return x;
  };

  Game_CharacterBase.prototype.cy = function(grid) {
    var y = this.collider('collision').center.y;
    if (grid) y /= QMovement.tileSize;
    return y;
  };
})();

//-----------------------------------------------------------------------------
// Game_Character

(function() {
  var Alias_Game_Character_processMoveCommand = Game_Character.prototype.processMoveCommand;
  Game_Character.prototype.processMoveCommand = function(command) {
    this.subMVMoveCommands(command);
    if (this.subQMoveCommand(command)) {
      command = this._moveRoute.list[this._moveRouteIndex];
    }
    this.processQMoveCommands(command);
    Alias_Game_Character_processMoveCommand.call(this, command);
  };

  Game_Character.prototype.subMVMoveCommands = function(command) {
    var gc = Game_Character;
    var params = command.parameters;
    switch (command.code) {
      case gc.ROUTE_MOVE_DOWN: {
        this.subQMove('2, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_LEFT: {
        this.subQMove('4, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_RIGHT: {
        this.subQMove('6, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_UP: {
        this.subQMove('8, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_LOWER_L: {
        this.subQMove('1, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_LOWER_R: {
        this.subQMove('3, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_UPPER_L: {
        this.subQMove('7, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_UPPER_R: {
        this.subQMove('9, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_FORWARD: {
        this.subQMove('5, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_MOVE_BACKWARD: {
        this.subQMove('0, 1,' + QMovement.tileSize);
        break;
      }
      case gc.ROUTE_TURN_DOWN:
      case gc.ROUTE_TURN_LEFT:
      case gc.ROUTE_TURN_RIGHT:
      case gc.ROUTE_TURN_UP:
      case gc.ROUTE_TURN_90D_R:
      case gc.ROUTE_TURN_90D_L:
      case gc.ROUTE_TURN_180D:
      case gc.ROUTE_TURN_90D_R_L:
      case gc.ROUTE_TURN_RANDOM:
      case gc.ROUTE_TURN_TOWARD:
      case gc.ROUTE_TURN_AWAY: {
        this._freqCount = this.freqThreshold();
        break;
      }
    }
  };

  Game_Character.prototype.subQMoveCommand = function(command) {
    var gc = Game_Character;
    var code = command.code;
    var params = command.parameters;
    if (command.code === gc.ROUTE_SCRIPT) {
      var qmove = /^qmove\((.*)\)/i.exec(params[0]);
      var qmove2 = /^qmove2\((.*)\)/i.exec(params[0]);
      var arc = /^arc\((.*)\)/i.exec(params[0]);
      var arc2 = /^arc2\((.*)\)/i.exec(params[0]);
      if (qmove) return this.subQMove(qmove[1]);
      if (qmove2) return this.subQMove2(qmove2[1]);
      if (arc) return this.subArc(arc[1]);
      if (arc2) return this.subArc2(arc2[1]);
    }
    return false;
  };

  Game_Character.prototype.processQMoveCommands = function(command) {
    var params = command.parameters;
    switch (command.code) {
      case 'arc': {
        this.arc(params[0], params[1], eval(params[2]), params[3], params[4]);
        break;
      }
      case 'arc2': {
        var x = params[0] + this.px;
        var y = params[1] + this.py;
        this.arc(x, y, eval(params[2]), params[3], params[4]);
        break;
      }
      case 'fixedRadianMove': {
        this.fixedRadianMove(params[0], params[1]);
        break;
      }
      case 'fixedMove': {
        this.fixedMove(params[0], params[1]);
        break;
      }
      case 'fixedMoveBackward': {
        this.fixedMoveBackward(params[0]);
        break;
      }
      case 'fixedMoveForward': {
        this.fixedMove(this.direction(), params[0]);
        break;
      }
    }
  };

  Game_Character.prototype.subArc = function(settings) {
    var cmd = {};
    cmd.code = 'arc';
    cmd.parameters = QPlus.stringToAry(settings);
    this._moveRoute.list[this._moveRouteIndex] = cmd;
    return true;
  };

  Game_Character.prototype.subArc2 = function(settings) {
    var cmd = {};
    cmd.code = 'arc2';
    cmd.parameters = QPlus.stringToAry(settings);
    this._moveRoute.list[this._moveRouteIndex] = cmd;
    return true;
  };

  Game_Character.prototype.subQMove = function(settings) {
    settings = QPlus.stringToAry(settings);
    var dir = settings[0];
    var amt = settings[1];
    var multi = settings[2] || 1;
    var tot = amt * multi;
    var steps = Math.floor(tot / this.moveTiles());
    var moved = 0;
    var i;
    for (i = 0; i < steps; i++) {
      moved += this.moveTiles();
      var cmd = {};
      if (dir === 0) {
        cmd.code = 'fixedMoveBackward';
        cmd.parameters = [this.moveTiles()];
      } else if (dir === 5) {
        cmd.code = 'fixedMoveForward';
        cmd.parameters = [this.moveTiles()];
      } else {
        cmd.code = 'fixedMove';
        cmd.parameters = [dir, this.moveTiles()];
      }
      this._moveRoute.list.splice(this._moveRouteIndex + 1, 0, cmd);
    }
    if (moved < tot) {
      var cmd = {};
      if (dir === 0) {
        cmd.code = 'fixedMoveBackward';
        cmd.parameters = [this.moveTiles()];
      } else if (dir === 5) {
        cmd.code = 'fixedMoveForward';
        cmd.parameters = [this.moveTiles()];
      } else {
        cmd.code = 'fixedMove';
        cmd.parameters = [dir, this.moveTiles()];
      }
      this._moveRoute.list.splice(this._moveRouteIndex + 1 + i, 0, cmd);
    }
    this._moveRoute.list.splice(this._moveRouteIndex, 1);
    return true;
  };

  Game_Character.prototype.subQMove2 = function(settings) {
    settings = QPlus.stringToAry(settings);
    var radian = settings[0];
    var dist = settings[1];
    var maxSteps = Math.floor(dist / this.moveTiles());
    var steps = 0;
    var i;
    for (i = 0; i < maxSteps; i++) {
      steps += this.moveTiles();
      var cmd = {};
      cmd.code = 'fixedRadianMove';
      cmd.parameters = [radian, this.moveTiles()];
      this._moveRoute.list.splice(this._moveRouteIndex + 1, 0, cmd);
    }
    if (steps < dist) {
      var cmd = {};
      cmd.code = 'fixedRadianMove';
      cmd.parameters = [radian, dist - steps];
      this._moveRoute.list.splice(this._moveRouteIndex + 1 + i, 0, cmd);
    }
    this._moveRoute.list.splice(this._moveRouteIndex, 1);
    return true;
  };

  Game_Character.prototype.moveRandom = function() {
    var d = 2 + Math.randomInt(4) * 2;
    if (this.canPixelPass(this._px, this._py, d)) {
      this.moveStraight(d);
    }
  };

  var Alias_Game_Character_moveTowardCharacter = Game_Character.prototype.moveTowardCharacter;
  Game_Character.prototype.moveTowardCharacter = function(character) {
    if ($gameMap.offGrid()) {
      var dx = this.deltaPXFrom(character.cx());
      var dy = this.deltaPYFrom(character.cy());
      var radian = Math.atan2(-dy, -dx);
      if (radian < 0) radian += Math.PI * 2;
      var oldSM = this._smartMove;
      if (oldSM <= 1) this._smartMove = 2;
      this.moveRadian(radian);
      this._smartMove = oldSM;
    } else {
      Alias_Game_Character_moveTowardCharacter.call(this, character);
    }
  };

  var Alias_Game_Character_moveAwayFromCharacter = Game_Character.prototype.moveAwayFromCharacter;
  Game_Character.prototype.moveAwayFromCharacter = function(character) {
    if ($gameMap.offGrid()) {
      var dx = this.deltaPXFrom(character.cx());
      var dy = this.deltaPYFrom(character.cy());
      var radian = Math.atan2(dy, dx);
      if (radian < 0) radian += Math.PI * 2;
      var oldSM = this._smartMove;
      if (oldSM <= 1) this._smartMove = 2;
      this.moveRadian(radian);
      this._smartMove = oldSM;
    } else {
      Alias_Game_Character_moveAwayFromCharacter.call(this, character);
    }
  };

  Game_Character.prototype.turnTowardCharacter = function(character) {
    var dx = this.deltaPXFrom(character.cx());
    var dy = this.deltaPYFrom(character.cy());
    this.setRadian(Math.atan2(-dy, -dx));
  };

  Game_Character.prototype.turnTowardCharacterForward = function(character, dt) {
    if (!character.isMoving()) {
      return this.turnTowardCharacter(character);
    }
    dt = dt || 1;
    var forward = character.forwardV();
    var x = character.cx() + (forward.x * dt);
    var y = character.cy() + (forward.y * dt);
    var dx = this.deltaPXFrom(x);
    var dy = this.deltaPYFrom(y);
    this.setRadian(Math.atan2(-dy, -dx));
  };

  Game_Character.prototype.turnAwayFromCharacter = function(character) {
    var dx = this.deltaPXFrom(character.cx());
    var dy = this.deltaPYFrom(character.cy());
    this.setRadian(Math.atan2(dy, dx));
  };

  Game_Character.prototype.deltaPXFrom = function(x) {
    return $gameMap.deltaPX(this.cx(), x);
  };

  Game_Character.prototype.deltaPYFrom = function(y) {
    return $gameMap.deltaPY(this.cy(), y);
  };

  Game_Character.prototype.pixelDistanceFrom = function(x, y) {
    return $gameMap.distance(this.cx(), this.cy(), x, y);
  };

  // Returns the px, py needed for this character to be center aligned
  // with the character passed in (align is based off collision collider)
  Game_Character.prototype.centerWith = function(character) {
    var dx1 = this.cx() - this._px;
    var dy1 = this.cy() - this._py;
    var dx2 = character.cx() - character._px;
    var dy2 = character.cy() - character._py;
    var dx = dx2 - dx1;
    var dy = dy2 - dy1;
    return new Point(character._px + dx, character._py + dy);
  };

  Game_Character.prototype.centerWithCollider = function(collider) {
    var dx1 = this.cx() - this._px;
    var dy1 = this.cy() - this._py;
    var dx2 = collider.center.x - collider.x;
    var dy2 = collider.center.y - collider.y;
    var dx = dx2 - dx1;
    var dy = dy2 - dy1;
    return new Point(collider.x + dx, collider.y + dy);
  };

  Game_Character.prototype.adjustPosition = function(xf, yf) {
    var dx = xf - this._px;
    var dy = yf - this._py;
    var radian = Math.atan2(dy, dx);
    var distX = Math.cos(radian) * this.moveTiles();
    var distY = Math.sin(radian) * this.moveTiles();
    var final = new Point(xf, yf);
    while (!this.canPixelPass(final.x, final.y, 5, 'collision')) {
      final.x -= distX;
      final.y -= distY;
      dx = final.x - this._px;
      dy = final.y - this._py;
      if (Math.atan2(dy, dx) !== radian) {
        final.x = this._px;
        final.y = this._py;
        break;
      }
    }
    this.moveColliders(this._px, this._py);
    return final;
  };
})();

//-----------------------------------------------------------------------------
// Game_Player

(function() {
  var Alias_Game_Player_initMembers = Game_Player.prototype.initMembers;
  Game_Player.prototype.initMembers = function() {
    Alias_Game_Player_initMembers.call(this);
    this._lastMouseRequested = 0;
    this._requestMouseMove = false;
    this._movingWithMouse = false;
    this._smartMove = QMovement.smartMove;
  };

  Game_Player.prototype.defaultColliderConfig = function() {
    return QMovement.playerCollider;
  };

  var Alias_Game_Player_refresh = Game_Player.prototype.refresh;
  Game_Player.prototype.refresh = function() {
    this.reloadColliders();
    Alias_Game_Player_refresh.call(this);
  };

  Game_Player.prototype.requestMouseMove = function() {
    var currFrame = Graphics.frameCount;
    var dt = currFrame - this._lastMouseRequested;
    if (dt >= 5) {
      this._lastMouseRequested = currFrame;
      this._requestMouseMove = true;
    } else {
      this._requestMouseMove = false;
    }
  };

  Game_Player.prototype.moveByMouse = function(x, y) {
    if (this.triggerTouchAction()) {
      this.clearMouseMove();
      return false;
    }
    this._movingWithMouse = true;
    return true;
  };

  Game_Player.prototype.clearMouseMove = function() {
    this._requestMouseMove = false;
    this._movingWithMouse = false;
    $gameTemp.clearDestination();
  };

  Game_Player.prototype.moveByInput = function() {
    if (!this.startedMoving() && this.canMove()) {
      if (this.triggerAction()) return;
      var direction = QMovement.diagonal ? Input.dir8 : Input.dir4;
      if (direction > 0) {
        this.clearMouseMove();
      } else if ($gameTemp.isDestinationValid()) {
        if (!QMovement.moveOnClick) {
          $gameTemp.clearDestination();
          return;
        }
        this.requestMouseMove();
        if (this._requestMouseMove) {
          var x = $gameTemp.destinationPX();
          var y = $gameTemp.destinationPY();
          return this.moveByMouse(x, y);
        }
      }
      if (Imported.QInput && Input.preferGamepad() && $gameMap.offGrid()) {
        this.moveWithAnalog();
      } else {
        if ([4, 6].contains(direction)) {
          this.moveInputHorizontal(direction);
        } else if ([2, 8].contains(direction)) {
          this.moveInputVertical(direction);
        } else if ([1, 3, 7, 9].contains(direction) && QMovement.diagonal) {
          this.moveInputDiagonal(direction);
        }
      }
    }
  };

  Game_Player.prototype.moveInputHorizontal = function(dir) {
    this.moveStraight(dir);
  };

  Game_Player.prototype.moveInputVertical = function(dir) {
    this.moveStraight(dir);
  };

  Game_Player.prototype.moveInputDiagonal = function(dir) {
    var diag = {
      1: [4, 2], 3: [6, 2],
      7: [4, 8], 9: [6, 8]
    }
    this.moveDiagonally(diag[dir][0], diag[dir][1]);
  };

  Game_Player.prototype.moveWithAnalog = function() {
    var horz = Input._dirAxesA.x;
    var vert = Input._dirAxesA.y;
    if (horz === 0 && vert === 0) return;
    var radian = Math.atan2(vert, horz);
    radian += radian < 0 ? Math.PI * 2 : 0;
    this.moveRadian(radian);
  };

  Game_Player.prototype.update = function(sceneActive) {
    var lastScrolledX = this.scrolledX();
    var lastScrolledY = this.scrolledY();
    var wasMoving = this.isMoving();
    this.updateDashing();
    if (sceneActive) {
      this.moveByInput();
    }
    Game_Character.prototype.update.call(this);
    this.updateScroll(lastScrolledX, lastScrolledY);
    this.updateVehicle();
    if (!this.startedMoving()) this.updateNonmoving(wasMoving);
    this._followers.update();
  };

  Game_Player.prototype.updateNonmoving = function(wasMoving) {
    if (!$gameMap.isEventRunning()) {
      if (wasMoving) {
        if (this._freqCount >= this.freqThreshold()) {
          $gameParty.onPlayerWalk();
        }
        this.checkEventTriggerHere([1, 2]);
        if ($gameMap.setupStartingEvent()) return;
      }
      if (this.triggerAction()) return;
      if (wasMoving) {
        if (this._freqCount >= this.freqThreshold()) {
          this.updateEncounterCount();
          this._freqCount = 0;
        }
      } else if (!this.isMoving() && !this._movingWithMouse) {
        $gameTemp.clearDestination();
      }
    }
  };

  Game_Player.prototype.updateDashing = function() {
    if (this.startedMoving()) return;
    if (this.canMove() && !this.isInVehicle() && !$gameMap.isDashDisabled()) {
      this._dashing = this.isDashButtonPressed() || $gameTemp.isDestinationValid();
    } else {
      this._dashing = false;
    }
  };

  Game_Player.prototype.startMapEvent = function(x, y, triggers, normal) {
    if (!$gameMap.isEventRunning()) {
      var collider = this.collider('interaction');
      var x1 = this._px;
      var y1 = this._py;
      collider.moveTo(x, y);
      var events = ColliderManager.getCharactersNear(collider, function(chara) {
        return this.collidesWithEvent(chara, 'interaction');
      }.bind(this))
      collider.moveTo(x1, y1);
      if (events.length === 0) {
        events = null;
        return;
      }
      var cx = this.cx();
      var cy = this.cy();
      events.sort(function(a, b) {
        return a.pixelDistanceFrom(cx, cy) - b.pixelDistanceFrom(cx, cy);
      })
      var event = events.shift();
      while (true) {
        if (event.isTriggerIn(triggers) && event.isNormalPriority() === normal) {
          event.start();
        }
        if (events.length === 0 || $gameMap.isAnyEventStarting()) {
          break;
        }
        event = events.shift();
      }
      events = null;
    }
  };

  Game_Player.prototype.collidesWithEvent = function(event, type) {
    if (event.constructor === Game_Event && !event._erased) {
      return event.collider('interaction').intersects(this.collider(type));
    }
    return false;
  };

  Game_Player.prototype.checkEventTriggerHere = function(triggers) {
    if (this.canStartLocalEvents()) {
      this.startMapEvent(this.collider('interaction').x, this.collider('interaction').y, triggers, false);
    }
  };

  Game_Player.prototype.checkEventTriggerThere = function(triggers, x2, y2) {
    if (this.canStartLocalEvents()) {
      var direction = this.direction();
      var x1 = this.collider('interaction').x;
      var y1 = this.collider('interaction').y;
      x2 = x2 || $gameMap.roundPXWithDirection(x1, direction, this.moveTiles());
      y2 = y2 || $gameMap.roundPYWithDirection(y1, direction, this.moveTiles());
      this.startMapEvent(x2, y2, triggers, true);
      if (!$gameMap.isAnyEventStarting()) {
        return this.checkCounter(triggers);
      }
    }
  };

  Game_Player.prototype.triggerTouchAction = function() {
    if ($gameTemp.isDestinationValid()) {
      var dist = this.pixelDistanceFrom($gameTemp.destinationPX(), $gameTemp.destinationPY());
      if (dist <= QMovement.tileSize * 1.5) {
        var dx = $gameTemp.destinationPX() - this.cx();
        var dy = $gameTemp.destinationPY() - this.cy();
        if (Math.abs(dx) < this.moveTiles() / 2 && Math.abs(dy) < this.moveTiles() / 2) {
          return false;
        }
        var radian = Math.atan2(dy, dx);
        radian += radian < 0 ? 2 * Math.PI : 0;
        var dir = this.radianToDirection(radian, true);
        var horz = dir;
        var vert = dir;
        if ([1, 3, 7, 9].contains(dir)) {
          if (dir === 1 || dir === 7) horz = 4;
          if (dir === 1 || dir === 3) vert = 2;
          if (dir === 3 || dir === 9) horz = 6;
          if (dir === 7 || dir === 9) vert = 8;
        }
        var x1 = $gameMap.roundPXWithDirection(this._px, horz, this.moveTiles());
        var y1 = $gameMap.roundPYWithDirection(this._py, vert, this.moveTiles());
        this.startMapEvent(x1, y1, [0, 1, 2], true);
        if (!$gameMap.isAnyEventStarting()) {
          if (this.checkCounter([0, 1, 2], $gameTemp.destinationPX(), $gameTemp.destinationPY())) {
            this.clearMouseMove();
            this.setDirection(dir);
            return true;
          }
        } else {
          this.clearMouseMove();
          this.setDirection(dir);
          return true;
        }
      }
    }
    return false;
  };

  Game_Player.prototype.checkCounter = function(triggers, x2, y2) {
    var direction = this.direction();
    var x1 = this._px;
    var y1 = this._py;
    x2 = x2 || $gameMap.roundPXWithDirection(x1, direction, this.moveTiles());
    y2 = y2 || $gameMap.roundPYWithDirection(y1, direction, this.moveTiles());
    var collider = this.collider('interaction');
    collider.moveTo(x2, y2);
    var counter;
    ColliderManager.getCollidersNear(collider, function(tile) {
      if (!tile.isTile) return false;
      if (tile.isCounter && tile.intersects(collider)) {
        counter = tile;
        return 'break';
      }
      return false;
    })
    collider.moveTo(x1, y1);
    if (counter) {
      if ([4, 6].contains(direction)) {
        var dist = Math.abs(counter.center.x - collider.center.x);
        dist += collider.width;
      } else if ([8, 2].contains(direction)) {
        var dist = Math.abs(counter.center.y - collider.center.y);
        dist += collider.height;
      }
      var x3 = $gameMap.roundPXWithDirection(x1, direction, dist);
      var y3 = $gameMap.roundPYWithDirection(y1, direction, dist);
      return this.startMapEvent(x3, y3, triggers, true);
    }
    return false;
  };

  Game_Player.prototype.airshipHere = function() {
    // TODO
    return false;
  };

  Game_Player.prototype.shipBoatThere = function(x2, y2) {
    // TODO
    return false;
  };

  // TODO create follower support addon
  Game_Player.prototype.moveStraight = function(d, dist) {
    Game_Character.prototype.moveStraight.call(this, d, dist);
  };

  Game_Player.prototype.moveDiagonally = function(horz, vert) {
    Game_Character.prototype.moveDiagonally.call(this, horz, vert);
  };
})();

//-----------------------------------------------------------------------------
// Game_Event

(function() {
  var Alias_Game_Event_clearPageSettings = Game_Event.prototype.clearPageSettings;
  Game_Event.prototype.clearPageSettings = function() {
    Alias_Game_Event_clearPageSettings.call(this);
    this._ignoreCharacters = [];
  };

  var Alias_Game_Event_setupPageSettings = Game_Event.prototype.setupPageSettings;
  Game_Event.prototype.setupPageSettings = function() {
    Alias_Game_Event_setupPageSettings.call(this);
    this.reloadColliders();
    this.initialPosition();
    this._typeRandomDir = null;
    this._typeTowardPlayer = null;
    var notes = this.notes(true);
    var ignore = /<ignoreCharas:(.*?)>/i.exec(notes);
    this._ignoreCharacters = [];
    if (ignore) {
      this._ignoreCharacters = ignore[1].split(',').map(function(s) {
        return QPlus.charaIdToId(s);
      })
    }
  };

  Game_Event.prototype.initialPosition = function() {
    var ox = /<ox[=|:](-?[0-9]+)>/i.exec(this.comments(true)) || 0;
    var oy = /<oy[=|:](-?[0-9]+)>/i.exec(this.comments(true)) || 0;
    if (ox) ox = Number(ox[1]) || 0;
    if (oy) oy = Number(oy[1]) || 0;
    var nextOffset = new Point(ox, oy);
    if (this._initialOffset) {
      ox -= this._initialOffset.x;
      oy -= this._initialOffset.y;
    }
    this._initialOffset = nextOffset;
    this.setPixelPosition(this.px + ox, this.py + oy);
  };

  Game_Event.prototype.defaultColliderConfig = function() {
    return QMovement.eventCollider;
  };

  Game_Event.prototype.ignoreCharacters = function(type) {
    var ignores = Game_CharacterBase.prototype.ignoreCharacters.call(this, type);
    return ignores.concat(this._ignoreCharacters);
  };

  Game_Event.prototype.updateStop = function() {
    if (this._locked) {
      this._freqCount = this.freqThreshold();
      this.resetStopCount();
    }
    Game_Character.prototype.updateStop.call(this);
    if (!this.isMoveRouteForcing()) {
      this.updateSelfMovement();
    }
  };

  Game_Event.prototype.updateSelfMovement = function() {
    if (!this._locked && this.isNearTheScreen()) {
      if (this._freqCount < this.freqThreshold()) {
        switch (this._moveType) {
          case 1:
            this.moveTypeRandom();
            break;
          case 2:
            this.moveTypeTowardPlayer();
            break;
          case 3:
            this.moveTypeCustom();
            break;
        }
      } else if (this.checkStop(this.stopCountThreshold())) {
        this._freqCount = 0;
      }
    }
  };

  // TODO stop random dir from reseting every frame if event can't move
  Game_Event.prototype.moveTypeRandom = function() {
    if (this._freqCount === 0 || this._typeRandomDir === null) {
      this._typeRandomDir = 2 * (Math.randomInt(4) + 1);
    }
    if (!this.canPixelPass(this._px, this._py, this._typeRandomDir)) {
      this._typeRandomDir = 2 * (Math.randomInt(4) + 1);
    }
    this.moveStraight(this._typeRandomDir);
  };

  Game_Event.prototype.moveTypeTowardPlayer = function() {
    if (this.isNearThePlayer()) {
      if (this._freqCount === 0 || this._typeTowardPlayer === null) {
        this._typeTowardPlayer = Math.randomInt(6);
      }
      switch (this._typeTowardPlayer) {
        case 0: case 1: case 2: case 3: {
          this.moveTowardPlayer();
          break;
        }
        case 4: {
          this.moveTypeRandom();
          break;
        }
        case 5: {
          this.moveForward();
          break;
        }
      }
    } else {
      this.moveTypeRandom();
    }
  };

  Game_Event.prototype.checkEventTriggerTouch = function(x, y) {
    if (!$gameMap.isEventRunning()) {
      if (this._trigger === 2 && !this.isJumping() && this.isNormalPriority()) {
        var collider = this.collider('collision');
        var prevX = collider.x;
        var prevY = collider.y;
        collider.moveTo(x, y);
        var collided = false;
        ColliderManager.getCharactersNear(collider, (function(chara) {
          if (chara.constructor !== Game_Player) return false;
          collided = chara.collider('collision').intersects(collider);
          return 'break';
        }).bind(this));
        collider.moveTo(prevX, prevY);
        if (collided) {
          this._stopCount = 0;
          this._freqCount = this.freqThreshold();
          this.start();
        }
      }
    }
  };
})();

//-----------------------------------------------------------------------------
// Scene_Map

(function() {
  Input.keyMapper[121] = 'f10';

  var Alias_Scene_Map_updateMain = Scene_Map.prototype.updateMain;
  Scene_Map.prototype.updateMain = function() {
    Alias_Scene_Map_updateMain.call(this);
    var key = Imported.QInput ? '#f10' : 'f10';
    if ($gameTemp.isPlaytest() && Input.isTriggered(key)) {
      ColliderManager.toggle();
    }
    ColliderManager.update();
  };

  Scene_Map.prototype.processMapTouch = function() {
    if (TouchInput.isTriggered() || this._touchCount > 0) {
      if (TouchInput.isPressed()) {
        if (this._touchCount === 0 || this._touchCount >= 20) {
          var x = $gameMap.canvasToMapPX(TouchInput.x);
          var y = $gameMap.canvasToMapPY(TouchInput.y);
          if (!$gameMap.offGrid()) {
            var ox = x % QMovement.tileSize;
            var oy = y % QMovement.tileSize;
            x += QMovement.tileSize / 2 - ox;
            y += QMovement.tileSize / 2 - oy;
          }
          $gameTemp.setPixelDestination(x, y);
        }
        this._touchCount++;
      } else {
        this._touchCount = 0;
      }
    }
  };

  Scene_Map.prototype.updateCallMenu = function() {
    if (this.isMenuEnabled()) {
      if (this.isMenuCalled()) {
        this.menuCalling = true;
      }
      if (this.menuCalling && !$gamePlayer.startedMoving()) {
        this.callMenu();
      }
    } else {
      this.menuCalling = false;
    }
  };
})();

//-----------------------------------------------------------------------------
// Sprite_Collider

function Sprite_Collider() {
  this.initialize.apply(this, arguments);
}

(function() {
  Sprite_Collider.prototype = Object.create(Sprite.prototype);
  Sprite_Collider.prototype.constructor = Sprite_Collider;

  Sprite_Collider.prototype.initialize = function(collider, duration) {
    Sprite.prototype.initialize.call(this);
    this.z = 7;
    this._duration = duration || 0;
    this._cache = {};
    this.setupCollider(collider);
    this.checkChanges();
  };

  Sprite_Collider.prototype.setCache = function() {
    this._cache = {
      color: this._collider.color,
      width: this._collider.width,
      height: this._collider.height,
      radian: this._collider._radian
    }
  };

  Sprite_Collider.prototype.needsRedraw = function() {
    return this._cache.width !== this._collider.width ||
      this._cache.height !== this._collider.height ||
      this._cache.color !== this._collider.color ||
      this._cache.radian !== this._collider._radian
  };

  Sprite_Collider.prototype.setupCollider = function(collider) {
    this._collider = collider;
    var isNew = false;
    if (!this._colliderSprite) {
      this._colliderSprite = new PIXI.Graphics();
      isNew = true;
    }
    this.drawCollider();
    if (isNew) {
      this.addChild(this._colliderSprite);
    }
  };

  Sprite_Collider.prototype.drawCollider = function() {
    var collider = this._collider;
    this._colliderSprite.clear();
    var color = (collider.color || '#ff0000').replace('#', '');
    color = parseInt(color, 16);
    this._colliderSprite.beginFill(color);
    if (collider.isCircle()) {
      var radiusX = collider.radiusX;
      var radiusY = collider.radiusY;
      this._colliderSprite.drawEllipse(0, 0, radiusX, radiusY);
      this._colliderSprite.rotation = collider._radian;
    } else {
      this._colliderSprite.drawPolygon(collider._baseVertices);
    }
    this._colliderSprite.endFill();
  };

  Sprite_Collider.prototype.update = function() {
    Sprite.prototype.update.call(this);
    this.checkChanges();
    if (this._duration >= 0 || this._collider.kill) {
      this.updateDecay();
    }
  };

  Sprite_Collider.prototype.checkChanges = function() {
    this.visible = !this._collider._isHidden;
    this.x = this._collider.x + this._collider.ox;
    this.x -= $gameMap.displayX() * QMovement.tileSize;
    this.y = this._collider.y + this._collider.oy;
    this.y -= $gameMap.displayY() * QMovement.tileSize;
    if (this.x < -this._collider.width || this.x > Graphics.width) {
      if (this.y < -this._collider.height || this.y > Graphics.height) {
        this.visible = false;
      }
    }
    this._colliderSprite.z = this.z;
    this._colliderSprite.visible = this.visible;
    if (this.needsRedraw()) {
      this.drawCollider();
      this.setCache();
    }
  };

  Sprite_Collider.prototype.updateDecay = function() {
    this._duration--;
    if (this._duration <= 0 || this._collider.kill) {
      ColliderManager.removeSprite(this);
      this._collider = null;
    }
  };
})();

//-----------------------------------------------------------------------------
// Sprite_Destination
//
// The sprite for displaying the destination place of the touch input.

(function() {
  Sprite_Destination.prototype.updatePosition = function() {
    var tileWidth = $gameMap.tileWidth();
    var tileHeight = $gameMap.tileHeight();
    var x = $gameTemp.destinationPX();
    var y = $gameTemp.destinationPY();
    this.x = $gameMap.adjustPX(x);
    this.y = $gameMap.adjustPY(y);
  };
})();

//-----------------------------------------------------------------------------
// Spriteset_Map

(function() {
  var Alias_Spriteset_Map_createLowerLayer = Spriteset_Map.prototype.createLowerLayer;
  Spriteset_Map.prototype.createLowerLayer = function() {
    Alias_Spriteset_Map_createLowerLayer.call(this);
    if ($gameTemp.isPlaytest()) {
      this.createColliders();
    }
  };

  Spriteset_Map.prototype.createColliders = function() {
    this.addChild(ColliderManager.container);
    // also get collision map here
  };
})();
