text: //=============================================================================
  // rpg_managers.js v1.3.4
  //=============================================================================
  
  //-----------------------------------------------------------------------------
  // DataManager
  //
  // The static class that manages the database and game objects.
  
  function DataManager() {
      throw new Error('This is a static class');
  }
  
  var $dataActors       = null;
  var $dataClasses      = null;
  var $dataSkills       = null;
  var $dataItems        = null;
  var $dataWeapons      = null;
  var $dataArmors       = null;
  var $dataEnemies      = null;
  var $dataTroops       = null;
  var $dataStates       = null;
  var $dataAnimations   = null;
  var $dataTilesets     = null;
  var $dataCommonEvents = null;
  var $dataSystem       = null;
  var $dataMapInfos     = null;
  var $dataMap          = null;
  var $gameTemp         = null;
  var $gameSystem       = null;
  var $gameScreen       = null;
  var $gameTimer        = null;
  var $gameMessage      = null;
  var $gameSwitches     = null;
  var $gameVariables    = null;
  var $gameSelfSwitches = null;
  var $gameActors       = null;
  var $gameParty        = null;
  var $gameTroop        = null;
  var $gameMap          = null;
  var $gamePlayer       = null;
  var $testEvent        = null;
  
  DataManager._globalId       = 'RPGMV';
  DataManager._lastAccessedId = 1;
  DataManager._errorUrl       = null;
  
  DataManager._databaseFiles = [
      { name: '$dataActors',       src: 'Actors.json'       },
      { name: '$dataClasses',      src: 'Classes.json'      },
      { name: '$dataSkills',       src: 'Skills.json'       },
      { name: '$dataItems',        src: 'Items.json'        },
      { name: '$dataWeapons',      src: 'Weapons.json'      },
      { name: '$dataArmors',       src: 'Armors.json'       },
      { name: '$dataEnemies',      src: 'Enemies.json'      },
      { name: '$dataTroops',       src: 'Troops.json'       },
      { name: '$dataStates',       src: 'States.json'       },
      { name: '$dataAnimations',   src: 'Animations.json'   },
      { name: '$dataTilesets',     src: 'Tilesets.json'     },
      { name: '$dataCommonEvents', src: 'CommonEvents.json' },
      { name: '$dataSystem',       src: 'System.json'       },
      { name: '$dataMapInfos',     src: 'MapInfos.json'     }
  ];
  
  DataManager.loadDatabase = function() {
      var test = this.isBattleTest() || this.isEventTest();
      var prefix = test ? 'Test_' : '';
      for (var i = 0; i < this._databaseFiles.length; i++) {
          var name = this._databaseFiles[i].name;
          var src = this._databaseFiles[i].src;
          this.loadDataFile(name, prefix + src);
      }
      if (this.isEventTest()) {
          this.loadDataFile('$testEvent', prefix + 'Event.json');
      }
  };
  
  DataManager.loadDataFile = function(name, src) {
      var xhr = new XMLHttpRequest();
      var url = 'data/' + src;
      xhr.open('GET', url);
      xhr.overrideMimeType('application/json');
      xhr.onload = function() {
          if (xhr.status < 400) {
              window[name] = JSON.parse(xhr.responseText);
              DataManager.onLoad(window[name]);
          }
      };
      xhr.onerror = function() {
          DataManager._errorUrl = DataManager._errorUrl || url;
      };
      window[name] = null;
      xhr.send();
  };
  
  DataManager.isDatabaseLoaded = function() {
      this.checkError();
      for (var i = 0; i < this._databaseFiles.length; i++) {
          if (!window[this._databaseFiles[i].name]) {
              return false;
          }
      }
      return true;
  };
  
  DataManager.loadMapData = function(mapId) {
      if (mapId > 0) {
          var filename = 'Map%1.json'.format(mapId.padZero(3));
          this.loadDataFile('$dataMap', filename);
      } else {
          this.makeEmptyMap();
      }
  };
  
  DataManager.makeEmptyMap = function() {
      $dataMap = {};
      $dataMap.data = [];
      $dataMap.events = [];
      $dataMap.width = 100;
      $dataMap.height = 100;
      $dataMap.scrollType = 3;
  };
  
  DataManager.isMapLoaded = function() {
      this.checkError();
      return !!$dataMap;
  };
  
  DataManager.onLoad = function(object) {
      var array;
      if (object === $dataMap) {
          this.extractMetadata(object);
          array = object.events;
      } else {
          array = object;
      }
      if (Array.isArray(array)) {
          for (var i = 0; i < array.length; i++) {
              var data = array[i];
              if (data && data.note !== undefined) {
                  this.extractMetadata(data);
              }
          }
      }
      if (object === $dataSystem) {
          Decrypter.hasEncryptedImages = !!object.hasEncryptedImages;
          Decrypter.hasEncryptedAudio = !!object.hasEncryptedAudio;
          Scene_Boot.loadSystemImages();
      }
  };
  
  DataManager.extractMetadata = function(data) {
      var re = /<([^<>:]+)(:?)([^>]*)>/g;
      data.meta = {};
      for (;;) {
          var match = re.exec(data.note);
          if (match) {
              if (match[2] === ':') {
                  data.meta[match[1]] = match[3];
              } else {
                  data.meta[match[1]] = true;
              }
          } else {
              break;
          }
      }
  };
  
  DataManager.checkError = function() {
      if (DataManager._errorUrl) {
          throw new Error('Failed to load: ' + DataManager._errorUrl);
      }
  };
  
  DataManager.isBattleTest = function() {
      return Utils.isOptionValid('btest');
  };
  
  DataManager.isEventTest = function() {
      return Utils.isOptionValid('etest');
  };
  
  DataManager.isSkill = function(item) {
      return item && $dataSkills.contains(item);
  };
  
  DataManager.isItem = function(item) {
      return item && $dataItems.contains(item);
  };
  
  DataManager.isWeapon = function(item) {
      return item && $dataWeapons.contains(item);
  };
  
  DataManager.isArmor = function(item) {
      return item && $dataArmors.contains(item);
  };
  
  DataManager.createGameObjects = function() {
      $gameTemp          = new Game_Temp();
      $gameSystem        = new Game_System();
      $gameScreen        = new Game_Screen();
      $gameTimer         = new Game_Timer();
      $gameMessage       = new Game_Message();
      $gameSwitches      = new Game_Switches();
      $gameVariables     = new Game_Variables();
      $gameSelfSwitches  = new Game_SelfSwitches();
      $gameActors        = new Game_Actors();
      $gameParty         = new Game_Party();
      $gameTroop         = new Game_Troop();
      $gameMap           = new Game_Map();
      $gamePlayer        = new Game_Player();
  };
  
  DataManager.setupNewGame = function() {
      this.createGameObjects();
      this.selectSavefileForNewGame();
      $gameParty.setupStartingMembers();
      $gamePlayer.reserveTransfer($dataSystem.startMapId,
          $dataSystem.startX, $dataSystem.startY);
      Graphics.frameCount = 0;
  };
  
  DataManager.setupBattleTest = function() {
      this.createGameObjects();
      $gameParty.setupBattleTest();
      BattleManager.setup($dataSystem.testTroopId, true, false);
      BattleManager.setBattleTest(true);
      BattleManager.playBattleBgm();
  };
  
  DataManager.setupEventTest = function() {
      this.createGameObjects();
      this.selectSavefileForNewGame();
      $gameParty.setupStartingMembers();
      $gamePlayer.reserveTransfer(-1, 8, 6);
      $gamePlayer.setTransparent(false);
  };
  
  DataManager.loadGlobalInfo = function() {
      var json;
      try {
          json = StorageManager.load(0);
      } catch (e) {
          console.error(e);
          return [];
      }
      if (json) {
          var globalInfo = JSON.parse(json);
          for (var i = 1; i <= this.maxSavefiles(); i++) {
              if (!StorageManager.exists(i)) {
                  delete globalInfo[i];
              }
          }
          return globalInfo;
      } else {
          return [];
      }
  };
  
  DataManager.saveGlobalInfo = function(info) {
      StorageManager.save(0, JSON.stringify(info));
  };
  
  DataManager.isThisGameFile = function(savefileId) {
      var globalInfo = this.loadGlobalInfo();
      if (globalInfo && globalInfo[savefileId]) {
          if (StorageManager.isLocalMode()) {
              return true;
          } else {
              var savefile = globalInfo[savefileId];
              return (savefile.globalId === this._globalId &&
                      savefile.title === $dataSystem.gameTitle);
          }
      } else {
          return false;
      }
  };
  
  DataManager.isAnySavefileExists = function() {
      var globalInfo = this.loadGlobalInfo();
      if (globalInfo) {
          for (var i = 1; i < globalInfo.length; i++) {
              if (this.isThisGameFile(i)) {
                  return true;
              }
          }
      }
      return false;
  };
  
  DataManager.latestSavefileId = function() {
      var globalInfo = this.loadGlobalInfo();
      var savefileId = 1;
      var timestamp = 0;
      if (globalInfo) {
          for (var i = 1; i < globalInfo.length; i++) {
              if (this.isThisGameFile(i) && globalInfo[i].timestamp > timestamp) {
                  timestamp = globalInfo[i].timestamp;
                  savefileId = i;
              }
          }
      }
      return savefileId;
  };
  
  DataManager.loadAllSavefileImages = function() {
      var globalInfo = this.loadGlobalInfo();
      if (globalInfo) {
          for (var i = 1; i < globalInfo.length; i++) {
              if (this.isThisGameFile(i)) {
                  var info = globalInfo[i];
                  this.loadSavefileImages(info);
              }
          }
      }
  };
  
  DataManager.loadSavefileImages = function(info) {
      if (info.characters) {
          for (var i = 0; i < info.characters.length; i++) {
              ImageManager.loadCharacter(info.characters[i][0]);
          }
      }
      if (info.faces) {
          for (var j = 0; j < info.faces.length; j++) {
              ImageManager.loadFace(info.faces[j][0]);
          }
      }
  };
  
  DataManager.maxSavefiles = function() {
      return 20;
  };
  
  DataManager.saveGame = function(savefileId) {
      try {
          StorageManager.backup(savefileId);
          return this.saveGameWithoutRescue(savefileId);
      } catch (e) {
          console.error(e);
          try {
              StorageManager.remove(savefileId);
              StorageManager.restoreBackup(savefileId);
          } catch (e2) {
          }
          return false;
      }
  };
  
  DataManager.loadGame = function(savefileId) {
      try {
          return this.loadGameWithoutRescue(savefileId);
      } catch (e) {
          console.error(e);
          return false;
      }
  };
  
  DataManager.loadSavefileInfo = function(savefileId) {
      var globalInfo = this.loadGlobalInfo();
      return (globalInfo && globalInfo[savefileId]) ? globalInfo[savefileId] : null;
  };
  
  DataManager.lastAccessedSavefileId = function() {
      return this._lastAccessedId;
  };
  
  DataManager.saveGameWithoutRescue = function(savefileId) {
      var json = JsonEx.stringify(this.makeSaveContents());
      if (json.length >= 200000) {
          console.warn('Save data too big!');
      }
      StorageManager.save(savefileId, json);
      this._lastAccessedId = savefileId;
      var globalInfo = this.loadGlobalInfo() || [];
      globalInfo[savefileId] = this.makeSavefileInfo();
      this.saveGlobalInfo(globalInfo);
      return true;
  };
  
  DataManager.loadGameWithoutRescue = function(savefileId) {
      var globalInfo = this.loadGlobalInfo();
      if (this.isThisGameFile(savefileId)) {
          var json = StorageManager.load(savefileId);
          this.createGameObjects();
          this.extractSaveContents(JsonEx.parse(json));
          this._lastAccessedId = savefileId;
          return true;
      } else {
          return false;
      }
  };
  
  DataManager.selectSavefileForNewGame = function() {
      var globalInfo = this.loadGlobalInfo();
      this._lastAccessedId = 1;
      if (globalInfo) {
          var numSavefiles = Math.max(0, globalInfo.length - 1);
          if (numSavefiles < this.maxSavefiles()) {
              this._lastAccessedId = numSavefiles + 1;
          } else {
              var timestamp = Number.MAX_VALUE;
              for (var i = 1; i < globalInfo.length; i++) {
                  if (!globalInfo[i]) {
                      this._lastAccessedId = i;
                      break;
                  }
                  if (globalInfo[i].timestamp < timestamp) {
                      timestamp = globalInfo[i].timestamp;
                      this._lastAccessedId = i;
                  }
              }
          }
      }
  };
  
  DataManager.makeSavefileInfo = function() {
      var info = {};
      info.globalId   = this._globalId;
      info.title      = $dataSystem.gameTitle;
      info.characters = $gameParty.charactersForSavefile();
      info.faces      = $gameParty.facesForSavefile();
      info.playtime   = $gameSystem.playtimeText();
      info.timestamp  = Date.now();
      return info;
  };
  
  DataManager.makeSaveContents = function() {
      // A save data does not contain $gameTemp, $gameMessage, and $gameTroop.
      var contents = {};
      contents.system       = $gameSystem;
      contents.screen       = $gameScreen;
      contents.timer        = $gameTimer;
      contents.switches     = $gameSwitches;
      contents.variables    = $gameVariables;
      contents.selfSwitches = $gameSelfSwitches;
      contents.actors       = $gameActors;
      contents.party        = $gameParty;
      contents.map          = $gameMap;
      contents.player       = $gamePlayer;
      return contents;
  };
  
  DataManager.extractSaveContents = function(contents) {
      $gameSystem        = contents.system;
      $gameScreen        = contents.screen;
      $gameTimer         = contents.timer;
      $gameSwitches      = contents.switches;
      $gameVariables     = contents.variables;
      $gameSelfSwitches  = contents.selfSwitches;
      $gameActors        = contents.actors;
      $gameParty         = contents.party;
      $gameMap           = contents.map;
      $gamePlayer        = contents.player;
  };
  
  //-----------------------------------------------------------------------------
  // ConfigManager
  //
  // The static class that manages the configuration data.
  
  function ConfigManager() {
      throw new Error('This is a static class');
  }
  
  ConfigManager.alwaysDash        = false;
  ConfigManager.commandRemember   = false;
  
  Object.defineProperty(ConfigManager, 'bgmVolume', {
      get: function() {
          return AudioManager._bgmVolume;
      },
      set: function(value) {
          AudioManager.bgmVolume = value;
      },
      configurable: true
  });
  
  Object.defineProperty(ConfigManager, 'bgsVolume', {
      get: function() {
          return AudioManager.bgsVolume;
      },
      set: function(value) {
          AudioManager.bgsVolume = value;
      },
      configurable: true
  });
  
  Object.defineProperty(ConfigManager, 'meVolume', {
      get: function() {
          return AudioManager.meVolume;
      },
      set: function(value) {
          AudioManager.meVolume = value;
      },
      configurable: true
  });
  
  Object.defineProperty(ConfigManager, 'seVolume', {
      get: function() {
          return AudioManager.seVolume;
      },
      set: function(value) {
          AudioManager.seVolume = value;
      },
      configurable: true
  });
  
  ConfigManager.load = function() {
      var json;
      var config = {};
      try {
          json = StorageManager.load(-1);
      } catch (e) {
          console.error(e);
      }
      if (json) {
          config = JSON.parse(json);
      }
      this.applyData(config);
  };
  
  ConfigManager.save = function() {
      StorageManager.save(-1, JSON.stringify(this.makeData()));
  };
  
  ConfigManager.makeData = function() {
      var config = {};
      config.alwaysDash = this.alwaysDash;
      config.commandRemember = this.commandRemember;
      config.bgmVolume = this.bgmVolume;
      config.bgsVolume = this.bgsVolume;
      config.meVolume = this.meVolume;
      config.seVolume = this.seVolume;
      return config;
  };
  
  ConfigManager.applyData = function(config) {
      this.alwaysDash = this.readFlag(config, 'alwaysDash');
      this.commandRemember = this.readFlag(config, 'commandRemember');
      this.bgmVolume = this.readVolume(config, 'bgmVolume');
      this.bgsVolume = this.readVolume(config, 'bgsVolume');
      this.meVolume = this.readVolume(config, 'meVolume');
      this.seVolume = this.readVolume(config, 'seVolume');
  };
  
  ConfigManager.readFlag = function(config, name) {
      return !!config[name];
  };
  
  ConfigManager.readVolume = function(config, name) {
      var value = config[name];
      if (value !== undefined) {
          return Number(value).clamp(0, 100);
      } else {
          return 100;
      }
  };
  
  //-----------------------------------------------------------------------------
  // StorageManager
  //
  // The static class that manages storage for saving game data.
  
  function StorageManager() {
      throw new Error('This is a static class');
  }
  
  StorageManager.save = function(savefileId, json) {
      if (this.isLocalMode()) {
          this.saveToLocalFile(savefileId, json);
      } else {
          this.saveToWebStorage(savefileId, json);
      }
  };
  
  StorageManager.load = function(savefileId) {
      if (this.isLocalMode()) {
          return this.loadFromLocalFile(savefileId);
      } else {
          return this.loadFromWebStorage(savefileId);
      }
  };
  
  StorageManager.exists = function(savefileId) {
      if (this.isLocalMode()) {
          return this.localFileExists(savefileId);
      } else {
          return this.webStorageExists(savefileId);
      }
  };
  
  StorageManager.remove = function(savefileId) {
      if (this.isLocalMode()) {
          this.removeLocalFile(savefileId);
      } else {
          this.removeWebStorage(savefileId);
      }
  };
  
  StorageManager.backup = function(savefileId) {
      if (this.exists(savefileId)) {
          if (this.isLocalMode()) {
              var data = this.loadFromLocalFile(savefileId);
              var compressed = LZString.compressToBase64(data);
              var fs = require('fs');
              var dirPath = this.localFileDirectoryPath();
              var filePath = this.localFilePath(savefileId) + ".bak";
              if (!fs.existsSync(dirPath)) {
                  fs.mkdirSync(dirPath);
              }
              fs.writeFileSync(filePath, compressed);
          } else {
              var data = this.loadFromWebStorage(savefileId);
              var compressed = LZString.compressToBase64(data);
              var key = this.webStorageKey(savefileId) + "bak";
              localStorage.setItem(key, compressed);
          }
      }
  };
  
  StorageManager.backupExists = function(savefileId) {
      if (this.isLocalMode()) {
          return this.localFileBackupExists(savefileId);
      } else {
          return this.webStorageBackupExists(savefileId);
      }
  };
  
  StorageManager.cleanBackup = function(savefileId) {
  	if (this.backupExists(savefileId)) {
  		if (this.isLocalMode()) {
  			var fs = require('fs');
              var dirPath = this.localFileDirectoryPath();
              var filePath = this.localFilePath(savefileId);
              fs.unlinkSync(filePath + ".bak");
  		} else {
  		    var key = this.webStorageKey(savefileId);
  			localStorage.removeItem(key + "bak");
  		}
  	}
  };
  
  StorageManager.restoreBackup = function(savefileId) {
      if (this.backupExists(savefileId)) {
          if (this.isLocalMode()) {
              var data = this.loadFromLocalBackupFile(savefileId);
              var compressed = LZString.compressToBase64(data);
              var fs = require('fs');
              var dirPath = this.localFileDirectoryPath();
              var filePath = this.localFilePath(savefileId);
              if (!fs.existsSync(dirPath)) {
                  fs.mkdirSync(dirPath);
              }
              fs.writeFileSync(filePath, compressed);
              fs.unlinkSync(filePath + ".bak");
          } else {
              var data = this.loadFromWebStorageBackup(savefileId);
              var compressed = LZString.compressToBase64(data);
              var key = this.webStorageKey(savefileId);
              localStorage.setItem(key, compressed);
              localStorage.removeItem(key + "bak");
          }
      }
  };
  
  StorageManager.isLocalMode = function() {
      return Utils.isNwjs();
  };
  
  StorageManager.saveToLocalFile = function(savefileId, json) {
      var data = LZString.compressToBase64(json);
      var fs = require('fs');
      var dirPath = this.localFileDirectoryPath();
      var filePath = this.localFilePath(savefileId);
      if (!fs.existsSync(dirPath)) {
          fs.mkdirSync(dirPath);
      }
      fs.writeFileSync(filePath, data);
  };
  
  StorageManager.loadFromLocalFile = function(savefileId) {
      var data = null;
      var fs = require('fs');
      var filePath = this.localFilePath(savefileId);
      if (fs.existsSync(filePath)) {
          data = fs.readFileSync(filePath, { encoding: 'utf8' });
      }
      return LZString.decompressFromBase64(data);
  };
  
  StorageManager.loadFromLocalBackupFile = function(savefileId) {
      var data = null;
      var fs = require('fs');
      var filePath = this.localFilePath(savefileId) + ".bak";
      if (fs.existsSync(filePath)) {
          data = fs.readFileSync(filePath, { encoding: 'utf8' });
      }
      return LZString.decompressFromBase64(data);
  };
  
  StorageManager.localFileBackupExists = function(savefileId) {
      var fs = require('fs');
      return fs.existsSync(this.localFilePath(savefileId) + ".bak");
  };
  
  StorageManager.localFileExists = function(savefileId) {
      var fs = require('fs');
      return fs.existsSync(this.localFilePath(savefileId));
  };
  
  StorageManager.removeLocalFile = function(savefileId) {
      var fs = require('fs');
      var filePath = this.localFilePath(savefileId);
      if (fs.existsSync(filePath)) {
          fs.unlinkSync(filePath);
      }
  };
  
  StorageManager.saveToWebStorage = function(savefileId, json) {
      var key = this.webStorageKey(savefileId);
      var data = LZString.compressToBase64(json);
      localStorage.setItem(key, data);
  };
  
  StorageManager.loadFromWebStorage = function(savefileId) {
      var key = this.webStorageKey(savefileId);
      var data = localStorage.getItem(key);
      return LZString.decompressFromBase64(data);
  };
  
  StorageManager.loadFromWebStorageBackup = function(savefileId) {
      var key = this.webStorageKey(savefileId) + "bak";
      var data = localStorage.getItem(key);
      return LZString.decompressFromBase64(data);
  };
  
  StorageManager.webStorageBackupExists = function(savefileId) {
      var key = this.webStorageKey(savefileId) + "bak";
      return !!localStorage.getItem(key);
  };
  
  StorageManager.webStorageExists = function(savefileId) {
      var key = this.webStorageKey(savefileId);
      return !!localStorage.getItem(key);
  };
  
  StorageManager.removeWebStorage = function(savefileId) {
      var key = this.webStorageKey(savefileId);
      localStorage.removeItem(key);
  };
  
  StorageManager.localFileDirectoryPath = function() {
      var path = require('path');
  
      var base = path.dirname(process.mainModule.filename);
      return path.join(base, 'save/');
  };
  
  StorageManager.localFilePath = function(savefileId) {
      var name;
      if (savefileId < 0) {
          name = 'config.rpgsave';
      } else if (savefileId === 0) {
          name = 'global.rpgsave';
      } else {
          name = 'file%1.rpgsave'.format(savefileId);
      }
      return this.localFileDirectoryPath() + name;
  };
  
  StorageManager.webStorageKey = function(savefileId) {
      if (savefileId < 0) {
          return 'RPG Config';
      } else if (savefileId === 0) {
          return 'RPG Global';
      } else {
          return 'RPG File%1'.format(savefileId);
      }
  };
  
  //-----------------------------------------------------------------------------
  // ImageManager
  //
  // The static class that loads images, creates bitmap objects and retains them.
  
  function ImageManager() {
      throw new Error('This is a static class');
  }
  
  ImageManager.cache = new CacheMap(ImageManager);
  
  ImageManager.loadAnimation = function(filename, hue) {
      return this.loadBitmap('img/animations/', filename, hue, true);
  };
  
  ImageManager.loadBattleback1 = function(filename, hue) {
      return this.loadBitmap('img/battlebacks1/', filename, hue, true);
  };
  
  ImageManager.loadBattleback2 = function(filename, hue) {
      return this.loadBitmap('img/battlebacks2/', filename, hue, true);
  };
  
  ImageManager.loadEnemy = function(filename, hue) {
      return this.loadBitmap('img/enemies/', filename, hue, true);
  };
  
  ImageManager.loadCharacter = function(filename, hue) {
      return this.loadBitmap('img/characters/', filename, hue, false);
  };
  
  ImageManager.loadFace = function(filename, hue) {
      return this.loadBitmap('img/faces/', filename, hue, true);
  };
  
  ImageManager.loadParallax = function(filename, hue) {
      return this.loadBitmap('img/parallaxes/', filename, hue, true);
  };
  
  ImageManager.loadPicture = function(filename, hue) {
      return this.loadBitmap('img/pictures/', filename, hue, true);
  };
  
  ImageManager.loadSvActor = function(filename, hue) {
      return this.loadBitmap('img/sv_actors/', filename, hue, false);
  };
  
  ImageManager.loadSvEnemy = function(filename, hue) {
      return this.loadBitmap('img/sv_enemies/', filename, hue, true);
  };
  
  ImageManager.loadSystem = function(filename, hue) {
      return this.loadBitmap('img/system/', filename, hue, false);
  };
  
  ImageManager.loadTileset = function(filename, hue) {
      return this.loadBitmap('img/tilesets/', filename, hue, false);
  };
  
  ImageManager.loadTitle1 = function(filename, hue) {
      return this.loadBitmap('img/titles1/', filename, hue, true);
  };
  
  ImageManager.loadTitle2 = function(filename, hue) {
      return this.loadBitmap('img/titles2/', filename, hue, true);
  };
  
  ImageManager.loadBitmap = function(folder, filename, hue, smooth) {
      if (filename) {
          var path = folder + encodeURIComponent(filename) + '.png';
          var bitmap = this.loadNormalBitmap(path, hue || 0);
          bitmap.smooth = smooth;
          return bitmap;
      } else {
          return this.loadEmptyBitmap();
      }
  };
  
  ImageManager.loadEmptyBitmap = function() {
      var empty = this.cache.getItem('empty');
      if (!empty) {
          empty = new Bitmap();
          this.cache.setItem('empty', empty);
      }
      return empty;
  };
  
  ImageManager.loadNormalBitmap = function(path, hue) {
      var key = path + ':' + hue;
      var bitmap = this.cache.getItem(key);
      if (!bitmap) {
          bitmap = Bitmap.load(path);
          bitmap.addLoadListener(function() {
              bitmap.rotateHue(hue);
          });
          this.cache.setItem(key, bitmap);
      }
      return bitmap;
  };
  
  ImageManager.clear = function() {
      this.cache.clear();
  };
  
  ImageManager.isReady = function() {
      for (var key in this.cache._inner) {
          var bitmap = this.cache._inner[key].item;
          if (bitmap.isError()) {
              throw new Error('Failed to load: ' + bitmap.url);
          }
          if (!bitmap.isReady()) {
              return false;
          }
      }
      return true;
  };
  
  ImageManager.isObjectCharacter = function(filename) {
      var sign = filename.match(/^[\!\$]+/);
      return sign && sign[0].contains('!');
  };
  
  ImageManager.isBigCharacter = function(filename) {
      var sign = filename.match(/^[\!\$]+/);
      return sign && sign[0].contains('$');
  };
  
  ImageManager.isZeroParallax = function(filename) {
      return filename.charAt(0) === '!';
  };
  
  //-----------------------------------------------------------------------------
  // AudioManager
  //
  // The static class that handles BGM, BGS, ME and SE.
  
  function AudioManager() {
      throw new Error('This is a static class');
  }
  
  AudioManager._bgmVolume      = 100;
  AudioManager._bgsVolume      = 100;
  AudioManager._meVolume       = 100;
  AudioManager._seVolume       = 100;
  AudioManager._currentBgm     = null;
  AudioManager._currentBgs     = null;
  AudioManager._bgmBuffer      = null;
  AudioManager._bgsBuffer      = null;
  AudioManager._meBuffer       = null;
  AudioManager._seBuffers      = [];
  AudioManager._staticBuffers  = [];
  AudioManager._replayFadeTime = 0.5;
  AudioManager._path           = 'audio/';
  AudioManager._blobUrl        = null;
  
  Object.defineProperty(AudioManager, 'bgmVolume', {
      get: function() {
          return this._bgmVolume;
      },
      set: function(value) {
          this._bgmVolume = value;
          this.updateBgmParameters(this._currentBgm);
      },
      configurable: true
  });
  
  Object.defineProperty(AudioManager, 'bgsVolume', {
      get: function() {
          return this._bgsVolume;
      },
      set: function(value) {
          this._bgsVolume = value;
          this.updateBgsParameters(this._currentBgs);
      },
      configurable: true
  });
  
  Object.defineProperty(AudioManager, 'meVolume', {
      get: function() {
          return this._meVolume;
      },
      set: function(value) {
          this._meVolume = value;
          this.updateMeParameters(this._currentMe);
      },
      configurable: true
  });
  
  Object.defineProperty(AudioManager, 'seVolume', {
      get: function() {
          return this._seVolume;
      },
      set: function(value) {
          this._seVolume = value;
      },
      configurable: true
  });
  
  AudioManager.playBgm = function(bgm, pos) {
      if (this.isCurrentBgm(bgm)) {
          this.updateBgmParameters(bgm);
      } else {
          this.stopBgm();
          if (bgm.name) { 
              if(Decrypter.hasEncryptedAudio && this.shouldUseHtml5Audio()){
                  this.playEncryptedBgm(bgm, pos);
              }
              else {
                  this._bgmBuffer = this.createBuffer('bgm', bgm.name);
                  this.updateBgmParameters(bgm);
                  if (!this._meBuffer) {
                      this._bgmBuffer.play(true, pos || 0);
                  }
              }
          }
      }
      this.updateCurrentBgm(bgm, pos);
  };
  
  AudioManager.playEncryptedBgm = function(bgm, pos) {
      var ext = this.audioFileExt();
      var url = this._path + 'bgm/' + encodeURIComponent(bgm.name) + ext;
      url = Decrypter.extToEncryptExt(url);
      Decrypter.decryptHTML5Audio(url, bgm, pos);
  };
  
  AudioManager.createDecryptBuffer = function(url, bgm, pos){
      this._blobUrl = url;
      this._bgmBuffer = this.createBuffer('bgm', bgm.name);
      this.updateBgmParameters(bgm);
      if (!this._meBuffer) {
          this._bgmBuffer.play(true, pos || 0);
      }
      this.updateCurrentBgm(bgm, pos);
  };
  
  AudioManager.replayBgm = function(bgm) {
      if (this.isCurrentBgm(bgm)) {
          this.updateBgmParameters(bgm);
      } else {
          this.playBgm(bgm, bgm.pos);
          if (this._bgmBuffer) {
              this._bgmBuffer.fadeIn(this._replayFadeTime);
          }
      }
  };
  
  AudioManager.isCurrentBgm = function(bgm) {
      return (this._currentBgm && this._bgmBuffer &&
              this._currentBgm.name === bgm.name);
  };
  
  AudioManager.updateBgmParameters = function(bgm) {
      this.updateBufferParameters(this._bgmBuffer, this._bgmVolume, bgm);
  };
  
  AudioManager.updateCurrentBgm = function(bgm, pos) {
      this._currentBgm = {
          name: bgm.name,
          volume: bgm.volume,
          pitch: bgm.pitch,
          pan: bgm.pan,
          pos: pos
      };
  };
  
  AudioManager.stopBgm = function() {
      if (this._bgmBuffer) {
          this._bgmBuffer.stop();
          this._bgmBuffer = null;
          this._currentBgm = null;
      }
  };
  
  AudioManager.fadeOutBgm = function(duration) {
      if (this._bgmBuffer && this._currentBgm) {
          this._bgmBuffer.fadeOut(duration);
          this._currentBgm = null;
      }
  };
  
  AudioManager.fadeInBgm = function(duration) {
      if (this._bgmBuffer && this._currentBgm) {
          this._bgmBuffer.fadeIn(duration);
      }
  };
  
  AudioManager.playBgs = function(bgs, pos) {
      if (this.isCurrentBgs(bgs)) {
          this.updateBgsParameters(bgs);
      } else {
          this.stopBgs();
          if (bgs.name) {
              this._bgsBuffer = this.createBuffer('bgs', bgs.name);
              this.updateBgsParameters(bgs);
              this._bgsBuffer.play(true, pos || 0);
          }
      }
      this.updateCurrentBgs(bgs, pos);
  };
  
  AudioManager.replayBgs = function(bgs) {
      if (this.isCurrentBgs(bgs)) {
          this.updateBgsParameters(bgs);
      } else {
          this.playBgs(bgs, bgs.pos);
          if (this._bgsBuffer) {
              this._bgsBuffer.fadeIn(this._replayFadeTime);
          }
      }
  };
  
  AudioManager.isCurrentBgs = function(bgs) {
      return (this._currentBgs && this._bgsBuffer &&
              this._currentBgs.name === bgs.name);
  };
  
  AudioManager.updateBgsParameters = function(bgs) {
      this.updateBufferParameters(this._bgsBuffer, this._bgsVolume, bgs);
  };
  
  AudioManager.updateCurrentBgs = function(bgs, pos) {
      this._currentBgs = {
          name: bgs.name,
          volume: bgs.volume,
          pitch: bgs.pitch,
          pan: bgs.pan,
          pos: pos
      };
  };
  
  AudioManager.stopBgs = function() {
      if (this._bgsBuffer) {
          this._bgsBuffer.stop();
          this._bgsBuffer = null;
          this._currentBgs = null;
      }
  };
  
  AudioManager.fadeOutBgs = function(duration) {
      if (this._bgsBuffer && this._currentBgs) {
          this._bgsBuffer.fadeOut(duration);
          this._currentBgs = null;
      }
  };
  
  AudioManager.fadeInBgs = function(duration) {
      if (this._bgsBuffer && this._currentBgs) {
          this._bgsBuffer.fadeIn(duration);
      }
  };
  
  AudioManager.playMe = function(me) {
      this.stopMe();
      if (me.name) {
          if (this._bgmBuffer && this._currentBgm) {
              this._currentBgm.pos = this._bgmBuffer.seek();
              this._bgmBuffer.stop();
          }
          this._meBuffer = this.createBuffer('me', me.name);
          this.updateMeParameters(me);
          this._meBuffer.play(false);
          this._meBuffer.addStopListener(this.stopMe.bind(this));
      }
  };
  
  AudioManager.updateMeParameters = function(me) {
      this.updateBufferParameters(this._meBuffer, this._meVolume, me);
  };
  
  AudioManager.fadeOutMe = function(duration) {
      if (this._meBuffer) {
          this._meBuffer.fadeOut(duration);
      }
  };
  
  AudioManager.stopMe = function() {
      if (this._meBuffer) {
          this._meBuffer.stop();
          this._meBuffer = null;
          if (this._bgmBuffer && this._currentBgm && !this._bgmBuffer.isPlaying()) {
              this._bgmBuffer.play(true, this._currentBgm.pos);
              this._bgmBuffer.fadeIn(this._replayFadeTime);
          }
      }
  };
  
  AudioManager.playSe = function(se) {
      if (se.name) {
          this._seBuffers = this._seBuffers.filter(function(audio) {
              return audio.isPlaying();
          });
          var buffer = this.createBuffer('se', se.name);
          this.updateSeParameters(buffer, se);
          buffer.play(false);
          this._seBuffers.push(buffer);
      }
  };
  
  AudioManager.updateSeParameters = function(buffer, se) {
      this.updateBufferParameters(buffer, this._seVolume, se);
  };
  
  AudioManager.stopSe = function() {
      this._seBuffers.forEach(function(buffer) {
          buffer.stop();
      });
      this._seBuffers = [];
  };
  
  AudioManager.playStaticSe = function(se) {
      if (se.name) {
          this.loadStaticSe(se);
          for (var i = 0; i < this._staticBuffers.length; i++) {
              var buffer = this._staticBuffers[i];
              if (buffer._reservedSeName === se.name) {
                  buffer.stop();
                  this.updateSeParameters(buffer, se);
                  buffer.play(false);
                  break;
              }
          }
      }
  };
  
  AudioManager.loadStaticSe = function(se) {
      if (se.name && !this.isStaticSe(se)) {
          var buffer = this.createBuffer('se', se.name);
          buffer._reservedSeName = se.name;
          this._staticBuffers.push(buffer);
          if (this.shouldUseHtml5Audio()) {
              Html5Audio.setStaticSe(buffer._url);
          }
      }
  };
  
  AudioManager.isStaticSe = function(se) {
      for (var i = 0; i < this._staticBuffers.length; i++) {
          var buffer = this._staticBuffers[i];
          if (buffer._reservedSeName === se.name) {
              return true;
          }
      }
      return false;
  };
  
  AudioManager.stopAll = function() {
      this.stopMe();
      this.stopBgm();
      this.stopBgs();
      this.stopSe();
  };
  
  AudioManager.saveBgm = function() {
      if (this._currentBgm) {
          var bgm = this._currentBgm;
          return {
              name: bgm.name,
              volume: bgm.volume,
              pitch: bgm.pitch,
              pan: bgm.pan,
              pos: this._bgmBuffer ? this._bgmBuffer.seek() : 0
          };
      } else {
          return this.makeEmptyAudioObject();
      }
  };
  
  AudioManager.saveBgs = function() {
      if (this._currentBgs) {
          var bgs = this._currentBgs;
          return {
              name: bgs.name,
              volume: bgs.volume,
              pitch: bgs.pitch,
              pan: bgs.pan,
              pos: this._bgsBuffer ? this._bgsBuffer.seek() : 0
          };
      } else {
          return this.makeEmptyAudioObject();
      }
  };
  
  AudioManager.makeEmptyAudioObject = function() {
      return { name: '', volume: 0, pitch: 0 };
  };
  
  AudioManager.createBuffer = function(folder, name) {
      var ext = this.audioFileExt();
      var url = this._path + folder + '/' + encodeURIComponent(name) + ext;
      if (this.shouldUseHtml5Audio() && folder === 'bgm') {
          if(this._blobUrl) Html5Audio.setup(this._blobUrl);
          else Html5Audio.setup(url);
          return Html5Audio;
      } else {
          return new WebAudio(url);
      }
  };
  
  AudioManager.updateBufferParameters = function(buffer, configVolume, audio) {
      if (buffer && audio) {
          buffer.volume = configVolume * (audio.volume || 0) / 10000;
          buffer.pitch = (audio.pitch || 0) / 100;
          buffer.pan = (audio.pan || 0) / 100;
      }
  };
  
  AudioManager.audioFileExt = function() {
      if (WebAudio.canPlayOgg() && !Utils.isMobileDevice()) {
          return '.ogg';
      } else {
          return '.m4a';
      }
  };
  
  AudioManager.shouldUseHtml5Audio = function() {
      // We use HTML5 Audio to play BGM instead of Web Audio API
      // because decodeAudioData() is very slow on Android Chrome.
      return Utils.isAndroidChrome() && !Decrypter.hasEncryptedAudio;
  };
  
  AudioManager.checkErrors = function() {
      this.checkWebAudioError(this._bgmBuffer);
      this.checkWebAudioError(this._bgsBuffer);
      this.checkWebAudioError(this._meBuffer);
      this._seBuffers.forEach(function(buffer) {
          this.checkWebAudioError(buffer);
      }.bind(this));
      this._staticBuffers.forEach(function(buffer) {
          this.checkWebAudioError(buffer);
      }.bind(this));
  };
  
  AudioManager.checkWebAudioError = function(webAudio) {
      if (webAudio && webAudio.isError()) {
          throw new Error('Failed to load: ' + webAudio.url);
      }
  };
  
  //-----------------------------------------------------------------------------
  // SoundManager
  //
  // The static class that plays sound effects defined in the database.
  
  function SoundManager() {
      throw new Error('This is a static class');
  }
  
  SoundManager.preloadImportantSounds = function() {
      this.loadSystemSound(0);
      this.loadSystemSound(1);
      this.loadSystemSound(2);
      this.loadSystemSound(3);
  };
  
  SoundManager.loadSystemSound = function(n) {
      if ($dataSystem) {
          AudioManager.loadStaticSe($dataSystem.sounds[n]);
      }
  };
  
  SoundManager.playSystemSound = function(n) {
      if ($dataSystem) {
          AudioManager.playStaticSe($dataSystem.sounds[n]);
      }
  };
  
  SoundManager.playCursor = function() {
      this.playSystemSound(0);
  };
  
  SoundManager.playOk = function() {
      this.playSystemSound(1);
  };
  
  SoundManager.playCancel = function() {
      this.playSystemSound(2);
  };
  
  SoundManager.playBuzzer = function() {
      this.playSystemSound(3);
  };
  
  SoundManager.playEquip = function() {
      this.playSystemSound(4);
  };
  
  SoundManager.playSave = function() {
      this.playSystemSound(5);
  };
  
  SoundManager.playLoad = function() {
      this.playSystemSound(6);
  };
  
  SoundManager.playBattleStart = function() {
      this.playSystemSound(7);
  };
  
  SoundManager.playEscape = function() {
      this.playSystemSound(8);
  };
  
  SoundManager.playEnemyAttack = function() {
      this.playSystemSound(9);
  };
  
  SoundManager.playEnemyDamage = function() {
      this.playSystemSound(10);
  };
  
  SoundManager.playEnemyCollapse = function() {
      this.playSystemSound(11);
  };
  
  SoundManager.playBossCollapse1 = function() {
      this.playSystemSound(12);
  };
  
  SoundManager.playBossCollapse2 = function() {
      this.playSystemSound(13);
  };
  
  SoundManager.playActorDamage = function() {
      this.playSystemSound(14);
  };
  
  SoundManager.playActorCollapse = function() {
      this.playSystemSound(15);
  };
  
  SoundManager.playRecovery = function() {
      this.playSystemSound(16);
  };
  
  SoundManager.playMiss = function() {
      this.playSystemSound(17);
  };
  
  SoundManager.playEvasion = function() {
      this.playSystemSound(18);
  };
  
  SoundManager.playMagicEvasion = function() {
      this.playSystemSound(19);
  };
  
  SoundManager.playReflection = function() {
      this.playSystemSound(20);
  };
  
  SoundManager.playShop = function() {
      this.playSystemSound(21);
  };
  
  SoundManager.playUseItem = function() {
      this.playSystemSound(22);
  };
  
  SoundManager.playUseSkill = function() {
      this.playSystemSound(23);
  };
  
  //-----------------------------------------------------------------------------
  // TextManager
  //
  // The static class that handles terms and messages.
  
  function TextManager() {
      throw new Error('This is a static class');
  }
  
  TextManager.basic = function(basicId) {
      return $dataSystem.terms.basic[basicId] || '';
  };
  
  TextManager.param = function(paramId) {
      return $dataSystem.terms.params[paramId] || '';
  };
  
  TextManager.command = function(commandId) {
      return $dataSystem.terms.commands[commandId] || '';
  };
  
  TextManager.message = function(messageId) {
      return $dataSystem.terms.messages[messageId] || '';
  };
  
  TextManager.getter = function(method, param) {
      return {
          get: function() {
              return this[method](param);
          },
          configurable: true
      };
  };
  
  Object.defineProperty(TextManager, 'currencyUnit', {
      get: function() { return $dataSystem.currencyUnit; },
      configurable: true
  });
  
  Object.defineProperties(TextManager, {
      level           : TextManager.getter('basic', 0),
      levelA          : TextManager.getter('basic', 1),
      hp              : TextManager.getter('basic', 2),
      hpA             : TextManager.getter('basic', 3),
      mp              : TextManager.getter('basic', 4),
      mpA             : TextManager.getter('basic', 5),
      tp              : TextManager.getter('basic', 6),
      tpA             : TextManager.getter('basic', 7),
      exp             : TextManager.getter('basic', 8),
      expA            : TextManager.getter('basic', 9),
      fight           : TextManager.getter('command', 0),
      escape          : TextManager.getter('command', 1),
      attack          : TextManager.getter('command', 2),
      guard           : TextManager.getter('command', 3),
      item            : TextManager.getter('command', 4),
      skill           : TextManager.getter('command', 5),
      equip           : TextManager.getter('command', 6),
      status          : TextManager.getter('command', 7),
      formation       : TextManager.getter('command', 8),
      save            : TextManager.getter('command', 9),
      gameEnd         : TextManager.getter('command', 10),
      options         : TextManager.getter('command', 11),
      weapon          : TextManager.getter('command', 12),
      armor           : TextManager.getter('command', 13),
      keyItem         : TextManager.getter('command', 14),
      equip2          : TextManager.getter('command', 15),
      optimize        : TextManager.getter('command', 16),
      clear           : TextManager.getter('command', 17),
      newGame         : TextManager.getter('command', 18),
      continue_       : TextManager.getter('command', 19),
      toTitle         : TextManager.getter('command', 21),
      cancel          : TextManager.getter('command', 22),
      buy             : TextManager.getter('command', 24),
      sell            : TextManager.getter('command', 25),
      alwaysDash      : TextManager.getter('message', 'alwaysDash'),
      commandRemember : TextManager.getter('message', 'commandRemember'),
      bgmVolume       : TextManager.getter('message', 'bgmVolume'),
      bgsVolume       : TextManager.getter('message', 'bgsVolume'),
      meVolume        : TextManager.getter('message', 'meVolume'),
      seVolume        : TextManager.getter('message', 'seVolume'),
      possession      : TextManager.getter('message', 'possession'),
      expTotal        : TextManager.getter('message', 'expTotal'),
      expNext         : TextManager.getter('message', 'expNext'),
      saveMessage     : TextManager.getter('message', 'saveMessage'),
      loadMessage     : TextManager.getter('message', 'loadMessage'),
      file            : TextManager.getter('message', 'file'),
      partyName       : TextManager.getter('message', 'partyName'),
      emerge          : TextManager.getter('message', 'emerge'),
      preemptive      : TextManager.getter('message', 'preemptive'),
      surprise        : TextManager.getter('message', 'surprise'),
      escapeStart     : TextManager.getter('message', 'escapeStart'),
      escapeFailure   : TextManager.getter('message', 'escapeFailure'),
      victory         : TextManager.getter('message', 'victory'),
      defeat          : TextManager.getter('message', 'defeat'),
      obtainExp       : TextManager.getter('message', 'obtainExp'),
      obtainGold      : TextManager.getter('message', 'obtainGold'),
      obtainItem      : TextManager.getter('message', 'obtainItem'),
      levelUp         : TextManager.getter('message', 'levelUp'),
      obtainSkill     : TextManager.getter('message', 'obtainSkill'),
      useItem         : TextManager.getter('message', 'useItem'),
      criticalToEnemy : TextManager.getter('message', 'criticalToEnemy'),
      criticalToActor : TextManager.getter('message', 'criticalToActor'),
      actorDamage     : TextManager.getter('message', 'actorDamage'),
      actorRecovery   : TextManager.getter('message', 'actorRecovery'),
      actorGain       : TextManager.getter('message', 'actorGain'),
      actorLoss       : TextManager.getter('message', 'actorLoss'),
      actorDrain      : TextManager.getter('message', 'actorDrain'),
      actorNoDamage   : TextManager.getter('message', 'actorNoDamage'),
      actorNoHit      : TextManager.getter('message', 'actorNoHit'),
      enemyDamage     : TextManager.getter('message', 'enemyDamage'),
      enemyRecovery   : TextManager.getter('message', 'enemyRecovery'),
      enemyGain       : TextManager.getter('message', 'enemyGain'),
      enemyLoss       : TextManager.getter('message', 'enemyLoss'),
      enemyDrain      : TextManager.getter('message', 'enemyDrain'),
      enemyNoDamage   : TextManager.getter('message', 'enemyNoDamage'),
      enemyNoHit      : TextManager.getter('message', 'enemyNoHit'),
      evasion         : TextManager.getter('message', 'evasion'),
      magicEvasion    : TextManager.getter('message', 'magicEvasion'),
      magicReflection : TextManager.getter('message', 'magicReflection'),
      counterAttack   : TextManager.getter('message', 'counterAttack'),
      substitute      : TextManager.getter('message', 'substitute'),
      buffAdd         : TextManager.getter('message', 'buffAdd'),
      debuffAdd       : TextManager.getter('message', 'debuffAdd'),
      buffRemove      : TextManager.getter('message', 'buffRemove'),
      actionFailure   : TextManager.getter('message', 'actionFailure'),
  });
  
  //-----------------------------------------------------------------------------
  // SceneManager
  //
  // The static class that manages scene transitions.
  
  function SceneManager() {
      throw new Error('This is a static class');
  }
  
  /*
   * Gets the current time in ms.
   * @private
   */
  SceneManager._getTimeInMs = function() {
      return performance.now();
  };
  
  SceneManager._scene             = null;
  SceneManager._nextScene         = null;
  SceneManager._stack             = [];
  SceneManager._stopped           = false;
  SceneManager._sceneStarted      = false;
  SceneManager._exiting           = false;
  SceneManager._previousClass     = null;
  SceneManager._backgroundBitmap  = null;
  SceneManager._screenWidth       = 816;
  SceneManager._screenHeight      = 624;
  SceneManager._boxWidth          = 816;
  SceneManager._boxHeight         = 624;
  SceneManager._deltaTime = 1.0 / 60.0;
  SceneManager._currentTime = SceneManager._getTimeInMs();
  SceneManager._accumulator = 0.0;
  
  SceneManager.run = function(sceneClass) {
      try {
          this.initialize();
          this.goto(sceneClass);
          this.requestUpdate();
      } catch (e) {
          this.catchException(e);
      }
  };
  
  SceneManager.initialize = function() {
      this.initGraphics();
      this.checkFileAccess();
      this.initAudio();
      this.initInput();
      this.initNwjs();
      this.checkPluginErrors();
      this.setupErrorHandlers();
  };
  
  SceneManager.initGraphics = function() {
      var type = this.preferableRendererType();
      Graphics.initialize(this._screenWidth, this._screenHeight, type);
      Graphics.boxWidth = this._boxWidth;
      Graphics.boxHeight = this._boxHeight;
      Graphics.setLoadingImage('img/system/Loading.png');
      if (Utils.isOptionValid('showfps')) {
          Graphics.showFps();
      }
      if (type === 'webgl') {
          this.checkWebGL();
      }
  };
  
  SceneManager.preferableRendererType = function() {
      if (Utils.isOptionValid('canvas')) {
          return 'canvas';
      } else if (Utils.isOptionValid('webgl')) {
          return 'webgl';
      } else if (this.shouldUseCanvasRenderer()) {
          return 'canvas';
      } else {
          return 'auto';
      }
  };
  
  SceneManager.shouldUseCanvasRenderer = function() {
      return Utils.isMobileDevice();
  };
  
  SceneManager.checkWebGL = function() {
      if (!Graphics.hasWebGL()) {
          throw new Error('Your browser does not support WebGL.');
      }
  };
  
  SceneManager.checkFileAccess = function() {
      if (!Utils.canReadGameFiles()) {
          throw new Error('Your browser does not allow to read local files.');
      }
  };
  
  SceneManager.initAudio = function() {
      var noAudio = Utils.isOptionValid('noaudio');
      if (!WebAudio.initialize(noAudio) && !noAudio) {
          throw new Error('Your browser does not support Web Audio API.');
      }
  };
  
  SceneManager.initInput = function() {
      Input.initialize();
      TouchInput.initialize();
  };
  
  SceneManager.initNwjs = function() {
      if (Utils.isNwjs()) {
          var gui = require('nw.gui');
          var win = gui.Window.get();
          if (process.platform === 'darwin' && !win.menu) {
              var menubar = new gui.Menu({ type: 'menubar' });
              var option = { hideEdit: true, hideWindow: true };
              menubar.createMacBuiltin('Game', option);
              win.menu = menubar;
          }
      }
  };
  
  SceneManager.checkPluginErrors = function() {
      PluginManager.checkErrors();
  };
  
  SceneManager.setupErrorHandlers = function() {
      window.addEventListener('error', this.onError.bind(this));
      document.addEventListener('keydown', this.onKeyDown.bind(this));
  };
  
  SceneManager.requestUpdate = function() {
      if (!this._stopped) {
          requestAnimationFrame(this.update.bind(this));
      }
  };
  
  SceneManager.update = function() {
      try {
          this.tickStart();
          if (Utils.isMobileSafari()) {
              this.updateInputData();
          }
          this.updateMain();
          this.tickEnd();
      } catch (e) {
          this.catchException(e);
      }
  };
  
  SceneManager.terminate = function() {
      window.close();
  };
  
  SceneManager.onError = function(e) {
      console.error(e.message);
      console.error(e.filename, e.lineno);
      try {
          this.stop();
          Graphics.printError('Error', e.message);
          AudioManager.stopAll();
      } catch (e2) {
      }
  };
  
  SceneManager.onKeyDown = function(event) {
      if (!event.ctrlKey && !event.altKey) {
          switch (event.keyCode) {
          case 116:   // F5
              if (Utils.isNwjs()) {
                  location.reload();
              }
              break;
          case 119:   // F8
              if (Utils.isNwjs() && Utils.isOptionValid('test')) {
                  require('nw.gui').Window.get().showDevTools();
              }
              break;
          }
      }
  };
  
  SceneManager.catchException = function(e) {
      if (e instanceof Error) {
          Graphics.printError(e.name, e.message);
          console.error(e.stack);
      } else {
          Graphics.printError('UnknownError', e);
      }
      AudioManager.stopAll();
      this.stop();
  };
  
  SceneManager.tickStart = function() {
      Graphics.tickStart();
  };
  
  SceneManager.tickEnd = function() {
      Graphics.tickEnd();
  };
  
  SceneManager.updateInputData = function() {
      Input.update();
      TouchInput.update();
  };
  
  SceneManager.updateMain = function() {
      if (Utils.isMobileSafari()) {
          this.changeScene();
          this.updateScene();
      } else {
          var newTime = this._getTimeInMs();
          var fTime = (newTime - this._currentTime) / 1000;
          if (fTime > 0.25) fTime = 0.25;
          this._currentTime = newTime;
          this._accumulator += fTime;
          while (this._accumulator >= this._deltaTime) {
              this.updateInputData();
              this.changeScene();
              this.updateScene();
              this._accumulator -= this._deltaTime;
          }
      }
      this.renderScene();
      this.requestUpdate();
  };
  
  SceneManager.updateManagers = function(ticks, delta) {
      ImageManager.cache.update(ticks, delta);
  };
  
  SceneManager.changeScene = function() {
      if (this.isSceneChanging() && !this.isCurrentSceneBusy()) {
          if (this._scene) {
              this._scene.terminate();
              this._previousClass = this._scene.constructor;
          }
          this._scene = this._nextScene;
          if (this._scene) {
              this._scene.create();
              this._nextScene = null;
              this._sceneStarted = false;
              this.onSceneCreate();
          }
          if (this._exiting) {
              this.terminate();
          }
      }
  };
  
  SceneManager.updateScene = function() {
      if (this._scene) {
          if (!this._sceneStarted && this._scene.isReady()) {
              this._scene.start();
              this._sceneStarted = true;
              this.onSceneStart();
          }
          if (this.isCurrentSceneStarted()) {
              this._scene.update();
          }
      }
  };
  
  SceneManager.renderScene = function() {
      if (this.isCurrentSceneStarted()) {
          Graphics.render(this._scene);
      } else if (this._scene) {
          this.onSceneLoading();
      }
  };
  
  SceneManager.onSceneCreate = function() {
      Graphics.startLoading();
  };
  
  SceneManager.onSceneStart = function() {
      Graphics.endLoading();
  };
  
  SceneManager.onSceneLoading = function() {
      Graphics.updateLoading();
  };
  
  SceneManager.isSceneChanging = function() {
      return this._exiting || !!this._nextScene;
  };
  
  SceneManager.isCurrentSceneBusy = function() {
      return this._scene && this._scene.isBusy();
  };
  
  SceneManager.isCurrentSceneStarted = function() {
      return this._scene && this._sceneStarted;
  };
  
  SceneManager.isNextScene = function(sceneClass) {
      return this._nextScene && this._nextScene.constructor === sceneClass;
  };
  
  SceneManager.isPreviousScene = function(sceneClass) {
      return this._previousClass === sceneClass;
  };
  
  SceneManager.goto = function(sceneClass) {
      if (sceneClass) {
          this._nextScene = new sceneClass();
      }
      if (this._scene) {
          this._scene.stop();
      }
  };
  
  SceneManager.push = function(sceneClass) {
      this._stack.push(this._scene.constructor);
      this.goto(sceneClass);
  };
  
  SceneManager.pop = function() {
      if (this._stack.length > 0) {
          this.goto(this._stack.pop());
      } else {
          this.exit();
      }
  };
  
  SceneManager.exit = function() {
      this.goto(null);
      this._exiting = true;
  };
  
  SceneManager.clearStack = function() {
      this._stack = [];
  };
  
  SceneManager.stop = function() {
      this._stopped = true;
  };
  
  SceneManager.prepareNextScene = function() {
      this._nextScene.prepare.apply(this._nextScene, arguments);
  };
  
  SceneManager.snap = function() {
      return Bitmap.snap(this._scene);
  };
  
  SceneManager.snapForBackground = function() {
      this._backgroundBitmap = this.snap();
      this._backgroundBitmap.blur();
  };
  
  SceneManager.backgroundBitmap = function() {
      return this._backgroundBitmap;
  };
  
  //-----------------------------------------------------------------------------
  // BattleManager
  //
  // The static class that manages battle progress.
  
  function BattleManager() {
      throw new Error('This is a static class');
  }
  
  BattleManager.setup = function(troopId, canEscape, canLose) {
      this.initMembers();
      this._canEscape = canEscape;
      this._canLose = canLose;
      $gameTroop.setup(troopId);
      $gameScreen.onBattleStart();
      this.makeEscapeRatio();
  };
  
  BattleManager.initMembers = function() {
      this._phase = 'init';
      this._canEscape = false;
      this._canLose = false;
      this._battleTest = false;
      this._eventCallback = null;
      this._preemptive = false;
      this._surprise = false;
      this._actorIndex = -1;
      this._actionForcedBattler = null;
      this._mapBgm = null;
      this._mapBgs = null;
      this._actionBattlers = [];
      this._subject = null;
      this._action = null;
      this._targets = [];
      this._logWindow = null;
      this._statusWindow = null;
      this._spriteset = null;
      this._escapeRatio = 0;
      this._escaped = false;
      this._rewards = {};
  };
  
  BattleManager.isBattleTest = function() {
      return this._battleTest;
  };
  
  BattleManager.setBattleTest = function(battleTest) {
      this._battleTest = battleTest;
  };
  
  BattleManager.setEventCallback = function(callback) {
      this._eventCallback = callback;
  };
  
  BattleManager.setLogWindow = function(logWindow) {
      this._logWindow = logWindow;
  };
  
  BattleManager.setStatusWindow = function(statusWindow) {
      this._statusWindow = statusWindow;
  };
  
  BattleManager.setSpriteset = function(spriteset) {
      this._spriteset = spriteset;
  };
  
  BattleManager.onEncounter = function() {
      this._preemptive = (Math.random() < this.ratePreemptive());
      this._surprise = (Math.random() < this.rateSurprise() && !this._preemptive);
  };
  
  BattleManager.ratePreemptive = function() {
      return $gameParty.ratePreemptive($gameTroop.agility());
  };
  
  BattleManager.rateSurprise = function() {
      return $gameParty.rateSurprise($gameTroop.agility());
  };
  
  BattleManager.saveBgmAndBgs = function() {
      this._mapBgm = AudioManager.saveBgm();
      this._mapBgs = AudioManager.saveBgs();
  };
  
  BattleManager.playBattleBgm = function() {
      AudioManager.playBgm($gameSystem.battleBgm());
      AudioManager.stopBgs();
  };
  
  BattleManager.playVictoryMe = function() {
      AudioManager.playMe($gameSystem.victoryMe());
  };
  
  BattleManager.playDefeatMe = function() {
      AudioManager.playMe($gameSystem.defeatMe());
  };
  
  BattleManager.replayBgmAndBgs = function() {
      if (this._mapBgm) {
          AudioManager.replayBgm(this._mapBgm);
      } else {
          AudioManager.stopBgm();
      }
      if (this._mapBgs) {
          AudioManager.replayBgs(this._mapBgs);
      }
  };
  
  BattleManager.makeEscapeRatio = function() {
      this._escapeRatio = 0.5 * $gameParty.agility() / $gameTroop.agility();
  };
  
  BattleManager.update = function() {
      if (!this.isBusy() && !this.updateEvent()) {
          switch (this._phase) {
          case 'start':
              this.startInput();
              break;
          case 'turn':
              this.updateTurn();
              break;
          case 'action':
              this.updateAction();
              break;
          case 'turnEnd':
              this.updateTurnEnd();
              break;
          case 'battleEnd':
              this.updateBattleEnd();
              break;
          }
      }
  };
  
  BattleManager.updateEvent = function() {
      switch (this._phase) {
          case 'start':
          case 'turn':
          case 'turnEnd':
              if (this.isActionForced()) {
                  this.processForcedAction();
                  return true;
              } else {
                  return this.updateEventMain();
              }
      }
      return this.checkAbort2();
  };
  
  BattleManager.updateEventMain = function() {
      $gameTroop.updateInterpreter();
      $gameParty.requestMotionRefresh();
      if ($gameTroop.isEventRunning() || this.checkBattleEnd()) {
          return true;
      }
      $gameTroop.setupBattleEvent();
      if ($gameTroop.isEventRunning() || SceneManager.isSceneChanging()) {
          return true;
      }
      return false;
  };
  
  BattleManager.isBusy = function() {
      return ($gameMessage.isBusy() || this._spriteset.isBusy() ||
              this._logWindow.isBusy());
  };
  
  BattleManager.isInputting = function() {
      return this._phase === 'input';
  };
  
  BattleManager.isInTurn = function() {
      return this._phase === 'turn';
  };
  
  BattleManager.isTurnEnd = function() {
      return this._phase === 'turnEnd';
  };
  
  BattleManager.isAborting = function() {
      return this._phase === 'aborting';
  };
  
  BattleManager.isBattleEnd = function() {
      return this._phase === 'battleEnd';
  };
  
  BattleManager.canEscape = function() {
      return this._canEscape;
  };
  
  BattleManager.canLose = function() {
      return this._canLose;
  };
  
  BattleManager.isEscaped = function() {
      return this._escaped;
  };
  
  BattleManager.actor = function() {
      return this._actorIndex >= 0 ? $gameParty.members()[this._actorIndex] : null;
  };
  
  BattleManager.clearActor = function() {
      this.changeActor(-1, '');
  };
  
  BattleManager.changeActor = function(newActorIndex, lastActorActionState) {
      var lastActor = this.actor();
      this._actorIndex = newActorIndex;
      var newActor = this.actor();
      if (lastActor) {
          lastActor.setActionState(lastActorActionState);
      }
      if (newActor) {
          newActor.setActionState('inputting');
      }
  };
  
  BattleManager.startBattle = function() {
      this._phase = 'start';
      $gameSystem.onBattleStart();
      $gameParty.onBattleStart();
      $gameTroop.onBattleStart();
      this.displayStartMessages();
  };
  
  BattleManager.displayStartMessages = function() {
      $gameTroop.enemyNames().forEach(function(name) {
          $gameMessage.add(TextManager.emerge.format(name));
      });
      if (this._preemptive) {
          $gameMessage.add(TextManager.preemptive.format($gameParty.name()));
      } else if (this._surprise) {
          $gameMessage.add(TextManager.surprise.format($gameParty.name()));
      }
  };
  
  BattleManager.startInput = function() {
      this._phase = 'input';
      $gameParty.makeActions();
      $gameTroop.makeActions();
      this.clearActor();
      if (this._surprise || !$gameParty.canInput()) {
          this.startTurn();
      }
  };
  
  BattleManager.inputtingAction = function() {
      return this.actor() ? this.actor().inputtingAction() : null;
  };
  
  BattleManager.selectNextCommand = function() {
      do {
          if (!this.actor() || !this.actor().selectNextCommand()) {
              this.changeActor(this._actorIndex + 1, 'waiting');
              if (this._actorIndex >= $gameParty.size()) {
                  this.startTurn();
                  break;
              }
          }
      } while (!this.actor().canInput());
  };
  
  BattleManager.selectPreviousCommand = function() {
      do {
          if (!this.actor() || !this.actor().selectPreviousCommand()) {
              this.changeActor(this._actorIndex - 1, 'undecided');
              if (this._actorIndex < 0) {
                  return;
              }
          }
      } while (!this.actor().canInput());
  };
  
  BattleManager.refreshStatus = function() {
      this._statusWindow.refresh();
  };
  
  BattleManager.startTurn = function() {
      this._phase = 'turn';
      this.clearActor();
      $gameTroop.increaseTurn();
      this.makeActionOrders();
      $gameParty.requestMotionRefresh();
      this._logWindow.startTurn();
  };
  
  BattleManager.updateTurn = function() {
      $gameParty.requestMotionRefresh();
      if (!this._subject) {
          this._subject = this.getNextSubject();
      }
      if (this._subject) {
          this.processTurn();
      } else {
          this.endTurn();
      }
  };
  
  BattleManager.processTurn = function() {
      var subject = this._subject;
      var action = subject.currentAction();
      if (action) {
          action.prepare();
          if (action.isValid()) {
              this.startAction();
          }
          subject.removeCurrentAction();
      } else {
          subject.onAllActionsEnd();
          this.refreshStatus();
          this._logWindow.displayAutoAffectedStatus(subject);
          this._logWindow.displayCurrentState(subject);
          this._logWindow.displayRegeneration(subject);
          this._subject = this.getNextSubject();
      }
  };
  
  BattleManager.endTurn = function() {
      this._phase = 'turnEnd';
      this._preemptive = false;
      this._surprise = false;
      this.allBattleMembers().forEach(function(battler) {
          battler.onTurnEnd();
          this.refreshStatus();
          this._logWindow.displayAutoAffectedStatus(battler);
          this._logWindow.displayRegeneration(battler);
      }, this);
  };
  
  BattleManager.updateTurnEnd = function() {
      this.startInput();
  };
  
  BattleManager.getNextSubject = function() {
      for (;;) {
          var battler = this._actionBattlers.shift();
          if (!battler) {
              return null;
          }
          if (battler.isBattleMember() && battler.isAlive()) {
              return battler;
          }
      }
  };
  
  BattleManager.allBattleMembers = function() {
      return $gameParty.members().concat($gameTroop.members());
  };
  
  BattleManager.makeActionOrders = function() {
      var battlers = [];
      if (!this._surprise) {
          battlers = battlers.concat($gameParty.members());
      }
      if (!this._preemptive) {
          battlers = battlers.concat($gameTroop.members());
      }
      battlers.forEach(function(battler) {
          battler.makeSpeed();
      });
      battlers.sort(function(a, b) {
          return b.speed() - a.speed();
      });
      this._actionBattlers = battlers;
  };
  
  BattleManager.startAction = function() {
      var subject = this._subject;
      var action = subject.currentAction();
      var targets = action.makeTargets();
      this._phase = 'action';
      this._action = action;
      this._targets = targets;
      subject.useItem(action.item());
      this._action.applyGlobal();
      this.refreshStatus();
      this._logWindow.startAction(subject, action, targets);
  };
  
  BattleManager.updateAction = function() {
      var target = this._targets.shift();
      if (target) {
          this.invokeAction(this._subject, target);
      } else {
          this.endAction();
      }
  };
  
  BattleManager.endAction = function() {
      this._logWindow.endAction(this._subject);
      this._phase = 'turn';
  };
  
  BattleManager.invokeAction = function(subject, target) {
      this._logWindow.push('pushBaseLine');
      if (Math.random() < this._action.itemCnt(target)) {
          this.invokeCounterAttack(subject, target);
      } else if (Math.random() < this._action.itemMrf(target)) {
          this.invokeMagicReflection(subject, target);
      } else {
          this.invokeNormalAction(subject, target);
      }
      subject.setLastTarget(target);
      this._logWindow.push('popBaseLine');
      this.refreshStatus();
  };
  
  BattleManager.invokeNormalAction = function(subject, target) {
      var realTarget = this.applySubstitute(target);
      this._action.apply(realTarget);
      this._logWindow.displayActionResults(subject, realTarget);
  };
  
  BattleManager.invokeCounterAttack = function(subject, target) {
      var action = new Game_Action(target);
      action.setAttack();
      action.apply(subject);
      this._logWindow.displayCounter(target);
      this._logWindow.displayActionResults(target, subject);
  };
  
  BattleManager.invokeMagicReflection = function(subject, target) {
  	this._action._reflectionTarget = target;
      this._logWindow.displayReflection(target);
      this._action.apply(subject);
      this._logWindow.displayActionResults(target, subject);
  };
  
  BattleManager.applySubstitute = function(target) {
      if (this.checkSubstitute(target)) {
          var substitute = target.friendsUnit().substituteBattler();
          if (substitute && target !== substitute) {
              this._logWindow.displaySubstitute(substitute, target);
              return substitute;
          }
      }
      return target;
  };
  
  BattleManager.checkSubstitute = function(target) {
      return target.isDying() && !this._action.isCertainHit();
  };
  
  BattleManager.isActionForced = function() {
      return !!this._actionForcedBattler;
  };
  
  BattleManager.forceAction = function(battler) {
      this._actionForcedBattler = battler;
      var index = this._actionBattlers.indexOf(battler);
      if (index >= 0) {
          this._actionBattlers.splice(index, 1);
      }
  };
  
  BattleManager.processForcedAction = function() {
      if (this._actionForcedBattler) {
          this._subject = this._actionForcedBattler;
          this._actionForcedBattler = null;
          this.startAction();
          this._subject.removeCurrentAction();
      }
  };
  
  BattleManager.abort = function() {
      this._phase = 'aborting';
  };
  
  BattleManager.checkBattleEnd = function() {
      if (this._phase) {
          if (this.checkAbort()) {
              return true;
          } else if ($gameParty.isAllDead()) {
              this.processDefeat();
              return true;
          } else if ($gameTroop.isAllDead()) {
              this.processVictory();
              return true;
          }
      }
      return false;
  };
  
  BattleManager.checkAbort = function() {
      if ($gameParty.isEmpty() || this.isAborting()) {
          this.processAbort();
          return true;
      }
      return false;
  };
  
  BattleManager.checkAbort2 = function() {
      if ($gameParty.isEmpty() || this.isAborting()) {
          SoundManager.playEscape();
          this._escaped = true;
          this.processAbort();
      }
      return false;
  };
  
  BattleManager.processVictory = function() {
      $gameParty.removeBattleStates();
      $gameParty.performVictory();
      this.playVictoryMe();
      this.replayBgmAndBgs();
      this.makeRewards();
      this.displayVictoryMessage();
      this.displayRewards();
      this.gainRewards();
      this.endBattle(0);
  };
  
  BattleManager.processEscape = function() {
      $gameParty.performEscape();
      SoundManager.playEscape();
      var success = this._preemptive ? true : (Math.random() < this._escapeRatio);
      if (success) {
          this.displayEscapeSuccessMessage();
          this._escaped = true;
          this.processAbort();
      } else {
          this.displayEscapeFailureMessage();
          this._escapeRatio += 0.1;
          $gameParty.clearActions();
          this.startTurn();
      }
      return success;
  };
  
  BattleManager.processAbort = function() {
      $gameParty.removeBattleStates();
      this.replayBgmAndBgs();
      this.endBattle(1);
  };
  
  BattleManager.processDefeat = function() {
      this.displayDefeatMessage();
      this.playDefeatMe();
      if (this._canLose) {
          this.replayBgmAndBgs();
      } else {
          AudioManager.stopBgm();
      }
      this.endBattle(2);
  };
  
  BattleManager.endBattle = function(result) {
      this._phase = 'battleEnd';
      if (this._eventCallback) {
          this._eventCallback(result);
      }
      if (result === 0) {
          $gameSystem.onBattleWin();
      } else if (this._escaped) {
          $gameSystem.onBattleEscape();
      }
  };
  
  BattleManager.updateBattleEnd = function() {
      if (this.isBattleTest()) {
          AudioManager.stopBgm();
          SceneManager.exit();
      } else if (!this._escaped && $gameParty.isAllDead()) {
          if (this._canLose) {
              $gameParty.reviveBattleMembers();
              SceneManager.pop();
          } else {
              SceneManager.goto(Scene_Gameover);
          }
      } else {
          SceneManager.pop();
      }
      this._phase = null;
  };
  
  BattleManager.makeRewards = function() {
      this._rewards = {};
      this._rewards.gold = $gameTroop.goldTotal();
      this._rewards.exp = $gameTroop.expTotal();
      this._rewards.items = $gameTroop.makeDropItems();
  };
  
  BattleManager.displayVictoryMessage = function() {
      $gameMessage.add(TextManager.victory.format($gameParty.name()));
  };
  
  BattleManager.displayDefeatMessage = function() {
      $gameMessage.add(TextManager.defeat.format($gameParty.name()));
  };
  
  BattleManager.displayEscapeSuccessMessage = function() {
      $gameMessage.add(TextManager.escapeStart.format($gameParty.name()));
  };
  
  BattleManager.displayEscapeFailureMessage = function() {
      $gameMessage.add(TextManager.escapeStart.format($gameParty.name()));
      $gameMessage.add('\\.' + TextManager.escapeFailure);
  };
  
  BattleManager.displayRewards = function() {
      this.displayExp();
      this.displayGold();
      this.displayDropItems();
  };
  
  BattleManager.displayExp = function() {
      var exp = this._rewards.exp;
      if (exp > 0) {
          var text = TextManager.obtainExp.format(exp, TextManager.exp);
          $gameMessage.add('\\.' + text);
      }
  };
  
  BattleManager.displayGold = function() {
      var gold = this._rewards.gold;
      if (gold > 0) {
          $gameMessage.add('\\.' + TextManager.obtainGold.format(gold));
      }
  };
  
  BattleManager.displayDropItems = function() {
      var items = this._rewards.items;
      if (items.length > 0) {
          $gameMessage.newPage();
          items.forEach(function(item) {
              $gameMessage.add(TextManager.obtainItem.format(item.name));
          });
      }
  };
  
  BattleManager.gainRewards = function() {
      this.gainExp();
      this.gainGold();
      this.gainDropItems();
  };
  
  BattleManager.gainExp = function() {
      var exp = this._rewards.exp;
      $gameParty.allMembers().forEach(function(actor) {
          actor.gainExp(exp);
      });
  };
  
  BattleManager.gainGold = function() {
      $gameParty.gainGold(this._rewards.gold);
  };
  
  BattleManager.gainDropItems = function() {
      var items = this._rewards.items;
      items.forEach(function(item) {
          $gameParty.gainItem(item, 1);
      });
  };
  
  //-----------------------------------------------------------------------------
  // PluginManager
  //
  // The static class that manages the plugins.
  
  function PluginManager() {
      throw new Error('This is a static class');
  }
  
  PluginManager._path         = 'js/plugins/';
  PluginManager._scripts      = [];
  PluginManager._errorUrls    = [];
  PluginManager._parameters   = {};
  
  PluginManager.setup = function(plugins) {
      plugins.forEach(function(plugin) {
          if (plugin.status && !this._scripts.contains(plugin.name)) {
              this.setParameters(plugin.name, plugin.parameters);
              this.loadScript(plugin.name + '.js');
              this._scripts.push(plugin.name);
          }
      }, this);
  };
  
  PluginManager.checkErrors = function() {
      var url = this._errorUrls.shift();
      if (url) {
          throw new Error('Failed to load: ' + url);
      }
  };
  
  PluginManager.parameters = function(name) {
      return this._parameters[name.toLowerCase()] || {};
  };
  
  PluginManager.setParameters = function(name, parameters) {
      this._parameters[name.toLowerCase()] = parameters;
  };
  
  PluginManager.loadScript = function(name) {
      var url = this._path + name;
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = url;
      script.async = false;
      script.onerror = this.onError.bind(this);
      script._url = url;
      document.body.appendChild(script);
  };
  
  PluginManager.onError = function(e) {
      this._errorUrls.push(e.target._url);
  };
  