/*
control.js:游戏主要逻辑控制
主要负责status相关内容,以及各种变量获取/存储
寻路算法和人物行走也在此文件内
 */

"use strict";

function control() {
  this._init();
}

control.prototype._init = function () {
  this.controldata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.control;
  this.renderFrameFuncs = [];
  this.replayActions = [];
  this.weathers = {};
  this.resizes = [];
  this.noAutoEvents = true;
  this.updateNextFrame = false;
  // --- 注册系统的animationFrame
  this.registerAnimationFrame(
    "totalTime",
    false,
    this._animationFrame_totalTime
  );
  this.registerAnimationFrame("autoSave", true, this._animationFrame_autoSave);
  this.registerAnimationFrame(
    "globalAnimate",
    true,
    this._animationFrame_globalAnimate
  );
  this.registerAnimationFrame("animate", true, this._animationFrame_animate);
  this.registerAnimationFrame(
    "heroMoving",
    true,
    this._animationFrame_heroMoving
  );
  this.registerAnimationFrame("weather", true, this._animationFrame_weather);
  this.registerAnimationFrame("tip", true, this._animateFrame_tip);
  this.registerAnimationFrame(
    "parallelDo",
    false,
    this._animationFrame_parallelDo
  );
  // --- 注册系统的天气
  this.registerWeather(
    "rain",
    this._weather_rain,
    this._animationFrame_weather_rain
  );
  this.registerWeather(
    "snow",
    this._weather_snow,
    this._animationFrame_weather_snow
  );
  this.registerWeather(
    "fog",
    this._weather_fog,
    this.__animateFrame_weather_image
  );
  this.registerWeather(
    "cloud",
    this._weather_cloud,
    this.__animateFrame_weather_image
  );
  this.registerWeather(
    "sun",
    this._weather_sun,
    this._animationFrame_weather_sun
  );
  // --- 注册系统的replay
  this.registerReplayAction("move", this._replayAction_move);
  this.registerReplayAction("item", this._replayAction_item);
  this.registerReplayAction("equip", this._replayAction_equip);
  this.registerReplayAction("unEquip", this._replayAction_unEquip);
  this.registerReplayAction("saveEquip", this._replayAction_saveEquip);
  this.registerReplayAction("loadEquip", this._replayAction_loadEquip);
  this.registerReplayAction("fly", this._replayAction_fly);
  this.registerReplayAction("shop", this._replayAction_shop);
  this.registerReplayAction("turn", this._replayAction_turn);
  this.registerReplayAction("getNext", this._replayAction_getNext);
  this.registerReplayAction("moveDirectly", this._replayAction_moveDirectly);
  this.registerReplayAction("key", this._replayAction_key);
  this.registerReplayAction("click", this._replayAction_click);
  this.registerReplayAction("ignoreInput", this._replayAction_ignoreInput);
  this.registerReplayAction("no", this._replayAction_no);
  // --- 注册系统的resize
  this.registerResize("gameGroup", this._resize_gameGroup);
  this.registerResize("canvas", this._resize_canvas);
  this.registerResize("statusBar", this._resize_statusBar);
  this.registerResize("status", this._resize_status);
  this.registerResize("toolBar", this._resize_toolBar);
  this.registerResize("tools", this._resize_tools);
};

// ------ requestAnimationFrame 相关 ------ //

////// 注册一个 animationFrame //////
// name:名称,可用来作为注销使用;needPlaying:是否只在游戏运行时才执行(在标题界面不执行)
// func:要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数
control.prototype.registerAnimationFrame = function (name, needPlaying, func) {
  this.unregisterAnimationFrame(name);
  this.renderFrameFuncs.push({
    name: name,
    needPlaying: needPlaying,
    func: func,
  });
};

////// 注销一个 animationFrame //////
control.prototype.unregisterAnimationFrame = function (name) {
  this.renderFrameFuncs = this.renderFrameFuncs.filter(function (x) {
    return x.name != name;
  });
};

////// 设置requestAnimationFrame //////
control.prototype._setRequestAnimationFrame = function () {
  this._checkRequestAnimationFrame();
  core.animateFrame.totalTime = Math.max(
    core.animateFrame.totalTime,
    core.getLocalStorage("totalTime", 0)
  );
  var loop = function (timestamp) {
    core.control.renderFrameFuncs.forEach(function (b) {
      if (b.func) {
        try {
          if (core.isPlaying() || !b.needPlaying)
            core.doFunc(b.func, core.control, timestamp);
        } catch (e) {
          console.error(e);
          console.error(
            "ERROR in requestAnimationFrame[" + b.name + "]:已自动注销该项。"
          );
          core.unregisterAnimationFrame(b.name);
        }
      }
    });
    window.requestAnimationFrame(loop);
  };
  window.requestAnimationFrame(loop);
};

control.prototype._checkRequestAnimationFrame = function () {
  (function () {
    var lastTime = 0;
    var vendors = ["webkit", "moz"];
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
      window.requestAnimationFrame =
        window[vendors[x] + "RequestAnimationFrame"];
      window.cancelAnimationFrame =
        window[vendors[x] + "CancelAnimationFrame"] || // Webkit中此取消方法的名字变了
        window[vendors[x] + "CancelRequestAnimationFrame"];
    }

    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = function (callback, element) {
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
        var id = window.setTimeout(function () {
          callback(currTime + timeToCall);
        }, timeToCall);
        lastTime = currTime + timeToCall;
        return id;
      };
    }
    if (!window.cancelAnimationFrame) {
      window.cancelAnimationFrame = function (id) {
        clearTimeout(id);
      };
    }
  })();
};

control.prototype._animationFrame_totalTime = function (timestamp) {
  core.animateFrame.totalTime += timestamp - core.animateFrame.totalTimeStart;
  core.animateFrame.totalTimeStart = timestamp;
  if (core.isPlaying()) {
    core.status.hero.statistics.totalTime = core.animateFrame.totalTime;
    core.status.hero.statistics.currTime +=
      timestamp - (core.status.hero.statistics.start || timestamp);
    core.status.hero.statistics.start = timestamp;
  }
};

control.prototype._animationFrame_autoSave = function (timestamp) {
  if (timestamp - core.saves.autosave.time <= 5000) return;
  core.control.checkAutosave();
  core.saves.autosave.time = timestamp;
};

control.prototype._animationFrame_globalAnimate = function (timestamp) {
  if (timestamp - core.animateFrame.globalTime <= core.values.animateSpeed)
    return;
  core.status.globalAnimateStatus++;
  if (core.status.floorId) {
    // Global Animate
    core.status.globalAnimateObjs.forEach(function (block) {
      core.drawBlock(block, core.status.globalAnimateStatus);
    });

    // Global floor images
    core.maps._drawFloorImages(
      core.status.floorId,
      core.canvas.bg,
      "bg",
      core.status.floorAnimateObjs || [],
      core.status.globalAnimateStatus
    );
    core.maps._drawFloorImages(
      core.status.floorId,
      core.canvas.fg,
      "fg",
      core.status.floorAnimateObjs || [],
      core.status.globalAnimateStatus
    );

    // Global Autotile Animate
    core.status.autotileAnimateObjs.forEach(function (block) {
      core.maps._drawAutotileAnimate(block, core.status.globalAnimateStatus);
    });

    // Global hero animate
    if (
      (core.status.hero || {}).animate &&
      core.status.heroMoving == 0 &&
      main.mode == "play" &&
      !core.status.preview.enabled
    ) {
      core.drawHero("stop", null, core.status.globalAnimateStatus);
    }
  }
  // Box animate
  core.drawBoxAnimate();
  core.animateFrame.globalTime = timestamp;
};

control.prototype._animationFrame_animate = function (timestamp) {
  if (
    timestamp - core.animateFrame.animateTime < 50 ||
    !core.status.animateObjs ||
    core.status.animateObjs.length == 0
  )
    return;
  core.clearMap("animate");
  // 更新帧
  for (var i = 0; i < core.status.animateObjs.length; i++) {
    var obj = core.status.animateObjs[i];
    if (obj.index == obj.animate.frames.length) {
      (function (callback) {
        setTimeout(function () {
          if (callback) callback();
        });
      })(obj.callback);
    }
  }
  core.status.animateObjs = core.status.animateObjs.filter(function (obj) {
    return obj.index < obj.animate.frames.length;
  });
  core.status.animateObjs.forEach(function (obj) {
    if (obj.hero) {
      core.maps._drawAnimateFrame(
        "animate",
        obj.animate,
        core.status.heroCenter.px,
        core.status.heroCenter.py,
        obj.index++
      );
    } else {
      core.maps._drawAnimateFrame(
        "animate",
        obj.animate,
        obj.centerX,
        obj.centerY,
        obj.index++
      );
    }
  });
  core.animateFrame.animateTime = timestamp;
};

control.prototype._animationFrame_heroMoving = function (timestamp) {
  if (core.status.heroMoving <= 0) return;
  // 换腿
  if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) {
    core.animateFrame.leftLeg = !core.animateFrame.leftLeg;
    core.animateFrame.moveTime = timestamp;
  }
  core.drawHero(
    core.animateFrame.leftLeg ? "leftFoot" : "rightFoot",
    4 * core.status.heroMoving
  );
};

control.prototype._animationFrame_weather = function (timestamp) {
  var weather = core.animateFrame.weather,
    type = weather.type;
  if (
    !core.dymCanvas.weather ||
    !core.control.weathers[type] ||
    !core.control.weathers[type].frameFunc
  )
    return;
  try {
    core.doFunc(
      core.control.weathers[type].frameFunc,
      core.control,
      timestamp,
      core.animateFrame.weather.level
    );
  } catch (e) {
    console.error(e);
    console.error("ERROR in weather[" + type + "]:已自动注销该项。");
    core.unregisterWeather(type);
  }
};

control.prototype._animationFrame_weather_rain = function (timestamp, level) {
  if (timestamp - core.animateFrame.weather.time < 30) return;
  var ctx = core.dymCanvas.weather,
    ox = core.bigmap.offsetX,
    oy = core.bigmap.offsetY;
  core.clearMap("weather");
  ctx.strokeStyle = "rgba(174,194,224,0.8)";
  ctx.lineWidth = 1;
  ctx.lineCap = "round";

  core.animateFrame.weather.nodes.forEach(function (p) {
    ctx.beginPath();
    ctx.moveTo(p.x - ox, p.y - oy);
    ctx.lineTo(p.x + p.l * p.xs - ox, p.y + p.l * p.ys - oy);
    ctx.stroke();

    p.x += p.xs;
    p.y += p.ys;
    if (p.x > core.bigmap.width * 32 || p.y > core.bigmap.height * 32) {
      p.x = Math.random() * core.bigmap.width * 32;
      p.y = -10;
    }
  });

  ctx.fill();
  core.animateFrame.weather.time = timestamp;
};

control.prototype._animationFrame_weather_snow = function (timestamp, level) {
  if (timestamp - core.animateFrame.weather.time < 30) return;
  var ctx = core.dymCanvas.weather,
    ox = core.bigmap.offsetX,
    oy = core.bigmap.offsetY;
  core.clearMap("weather");
  ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
  ctx.beginPath();
  core.animateFrame.weather.data = core.animateFrame.weather.data || 0;
  core.animateFrame.weather.data += 0.01;

  var angle = core.animateFrame.weather.data;
  core.animateFrame.weather.nodes.forEach(function (p) {
    ctx.moveTo(p.x - ox, p.y - oy);
    ctx.arc(p.x - ox, p.y - oy, p.r, 0, Math.PI * 2, true);
    // update
    p.x += Math.sin(angle) * core.animateFrame.weather.level;
    p.y += Math.cos(angle + p.d) + 1 + p.r / 2;
    if (
      p.x > core.bigmap.width * 32 + 5 ||
      p.x < -5 ||
      p.y > core.bigmap.height * 32
    ) {
      if (Math.random() > 1 / 3) {
        p.x = Math.random() * core.bigmap.width * 32;
        p.y = -10;
      } else {
        if (Math.sin(angle) > 0) p.x = -5;
        else p.x = core.bigmap.width * 32 + 5;
        p.y = Math.random() * core.bigmap.height * 32;
      }
    }
  });
  ctx.fill();
  core.animateFrame.weather.time = timestamp;
};

control.prototype.__animateFrame_weather_image = function (timestamp, level) {
  if (timestamp - core.animateFrame.weather.time < 30) return;
  var node = core.animateFrame.weather.nodes[0];
  var image = node.image;
  if (!image) return;
  core.clearMap("weather");
  core.setAlpha("weather", node.level / 500);
  var wind = 1.5;
  var width = image.width,
    height = image.height;
  node.x += node.dx * wind;
  node.y += (2 * node.dy - 1) * wind;
  if (node.x + 3 * width <= core._PX_) {
    node.x += 4 * width;
    while (node.x > 0) node.x -= width;
  }
  node.dy += node.delta;
  if (node.dy >= 1) {
    node.delta = -0.001;
  } else if (node.dy <= 0) {
    node.delta = 0.001;
  }
  if (node.y + 3 * height <= core._PY_) {
    node.y += 4 * height;
    while (node.y > 0) node.y -= height;
  } else if (node.y >= 0) {
    node.y -= height;
  }
  for (var i = 0; i < 3; ++i) {
    for (var j = 0; j < 3; ++j) {
      if (
        node.x + (i + 1) * width <= 0 ||
        node.x + i * width >= core._PX_ ||
        node.y + (j + 1) * height <= 0 ||
        node.y + j * height >= core._PY_
      )
        continue;
      core.drawImage("weather", image, node.x + i * width, node.y + j * height);
    }
  }
  core.setAlpha("weather", 1);
  core.animateFrame.weather.time = timestamp;
};

control.prototype._animationFrame_weather_sun = function (timestamp, level) {
  if (timestamp - core.animateFrame.weather.time < 30) return;
  var node = core.animateFrame.weather.nodes[0];
  var opacity = node.opacity + node.delta;
  if (opacity > level / 10 + 0.3 || opacity < level / 10 - 0.3)
    node.delta = -node.delta;
  node.opacity = opacity;
  core.setOpacity("weather", core.clamp(opacity, 0, 1));
  core.animateFrame.weather.time = timestamp;
};

control.prototype._animateFrame_tip = function (timestamp) {
  if (core.animateFrame.tip == null) return;
  var tip = core.animateFrame.tip;
  if (timestamp - tip.time <= 30) return;
  var delta = timestamp - tip.time;
  tip.time = timestamp;

  core.setFont("data", "16px Arial");
  core.setTextAlign("data", "left");
  core.clearMap("data", 0, 0, core._PX_, 50);
  core.ui._drawTip_drawOne(tip);
  if (tip.stage == 1) {
    tip.opacity += 0.05;
    if (tip.opacity >= 0.6) {
      tip.stage = 2;
      tip.displayTime = 0;
    }
  } else if (tip.stage == 2) {
    tip.displayTime += delta;
    if (tip.displayTime >= 1000) tip.stage = 3;
  } else tip.opacity -= 0.05;

  if (tip.opacity <= 0) {
    core.animateFrame.tip = null;
  }
};

control.prototype._animationFrame_parallelDo = function (timestamp) {
  core.control.controldata.parallelDo(timestamp);
};

// ------ 标题界面的处理 ------ //

////// 显示游戏开始界面 //////
control.prototype.showStartAnimate = function (noAnimate, callback) {
  this._showStartAnimate_resetDom();
  if (core.flags.startUsingCanvas || noAnimate)
    return this._showStartAnimate_finished(
      core.flags.startUsingCanvas,
      callback
    );
  core.hideWithAnimate(core.dom.startTop, 20, function () {
    core.control._showStartAnimate_finished(false, callback);
  });
};

control.prototype._showStartAnimate_resetDom = function () {
  core.dom.startPanel.style.opacity = 1;
  core.dom.startPanel.style.display = "block";
  core.dom.startTop.style.opacity = 1;
  core.dom.startTop.style.display = "block";
  core.dom.startButtonGroup.style.display = "none";
  core.dom.startButtons.style.display = "block";
  core.dom.levelChooseButtons.style.display = "none";
  core.status.played = false;
  core.clearStatus();
  core.clearMap("all");
  core.dom.musicBtn.style.display = "block";
  core.setMusicBtn();
  // 重置音量
  core.events.setVolume(1, 0);
  core.updateStatusBar();
};

control.prototype._showStartAnimate_finished = function (start, callback) {
  core.dom.startTop.style.display = "none";
  core.dom.startButtonGroup.style.display = "block";
  main.selectedButton = null;
  main.selectButton(0);
  if (start) core.startGame();
  if (callback) callback();
};

////// 隐藏游戏开始界面 //////
control.prototype.hideStartAnimate = function (callback) {
  core.hideWithAnimate(core.dom.startPanel, 20, callback);
};

////// 游戏是否已经开始 //////
control.prototype.isPlaying = function () {
  return core.status.played;
};

////// 清除游戏状态和数据 //////
control.prototype.clearStatus = function () {
  // 停止各个Timeout和Interval
  for (var i in core.timeout) {
    clearTimeout(core.timeout[i]);
    core.timeout[i] = null;
  }
  for (var i in core.interval) {
    clearInterval(core.interval[i]);
    core.interval[i] = null;
  }
  core.status = {};
  core.clearStatusBar();
  core.deleteAllCanvas();
  core.status.played = false;
};

control.prototype._initStatistics = function (totalTime) {
  if (!core.isset(core.status.hero.statistics))
    core.status.hero.statistics = {
      totalTime: totalTime,
      currTime: 0,
      hp: 0,
      battle: 0,
      money: 0,
      exp: 0,
      battleDamage: 0,
      poisonDamage: 0,
      extraDamage: 0,
      moveDirectly: 0,
      ignoreSteps: 0,
    };
};

// ------ 自动寻路,人物行走 ------ //

////// 清除自动寻路路线 //////
control.prototype.clearAutomaticRouteNode = function (x, y) {
  core.clearMap(
    "route",
    x * 32 + 5 - core.status.automaticRoute.offsetX,
    y * 32 + 5 - core.status.automaticRoute.offsetY,
    27,
    27
  );
};

////// 停止自动寻路操作 //////
control.prototype.stopAutomaticRoute = function () {
  if (!core.status.played) return;
  core.status.automaticRoute.autoHeroMove = false;
  core.status.automaticRoute.autoStep = 0;
  core.status.automaticRoute.destStep = 0;
  core.status.automaticRoute.movedStep = 0;
  core.status.automaticRoute.autoStepRoutes = [];
  core.status.automaticRoute.destX = null;
  core.status.automaticRoute.destY = null;
  core.status.automaticRoute.lastDirection = null;
  core.status.heroStop = true;
  if (core.status.automaticRoute.moveStepBeforeStop.length == 0)
    core.deleteCanvas("route");
};

////// 保存剩下的寻路,并停止 //////
control.prototype.saveAndStopAutomaticRoute = function () {
  var automaticRoute = core.status.automaticRoute;
  if (automaticRoute.moveStepBeforeStop.length == 0) {
    automaticRoute.moveStepBeforeStop = automaticRoute.autoStepRoutes.slice(
      automaticRoute.autoStep - 1
    );
    if (automaticRoute.moveStepBeforeStop.length >= 1)
      automaticRoute.moveStepBeforeStop[0].step -= automaticRoute.movedStep;
  }
  this.stopAutomaticRoute();
};

////// 继续剩下的自动寻路操作 //////
control.prototype.continueAutomaticRoute = function () {
  // 此函数只应由events.afterOpenDoor和events.afterBattle调用
  var moveStep = core.status.automaticRoute.moveStepBeforeStop;
  //core.status.automaticRoute.moveStepBeforeStop = [];
  if (
    moveStep.length === 0 ||
    (moveStep.length === 1 && moveStep[0].step === 1)
  ) {
    core.status.automaticRoute.moveStepBeforeStop = [];
  } else {
    core.setAutoHeroMove(moveStep);
  }
};

////// 清空剩下的自动寻路列表 //////
control.prototype.clearContinueAutomaticRoute = function (callback) {
  core.deleteCanvas("route");
  core.status.automaticRoute.moveStepBeforeStop = [];
  if (callback) callback();
};

////// 设置自动寻路路线 //////
control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) {
  if (!core.status.played || core.status.lockControl) return;
  if (this._setAutomaticRoute_isMoving(destX, destY)) return;
  if (this._setAutomaticRoute_isTurning(destX, destY, stepPostfix)) return;
  if (this._setAutomaticRoute_clickMoveDirectly(destX, destY, stepPostfix))
    return;
  // 找寻自动寻路路线
  var moveStep = core.automaticRoute(destX, destY);
  if (
    moveStep.length == 0 &&
    (destX != core.status.hero.loc.x ||
      destY != core.status.hero.loc.y ||
      stepPostfix.length == 0)
  )
    return;
  moveStep = moveStep.concat(stepPostfix);
  core.status.automaticRoute.destX = destX;
  core.status.automaticRoute.destY = destY;
  this._setAutomaticRoute_drawRoute(moveStep);
  this._setAutomaticRoute_setAutoSteps(moveStep);
  // 立刻移动
  core.setAutoHeroMove();
};

control.prototype._setAutomaticRoute_isMoving = function (destX, destY) {
  if (core.status.automaticRoute.autoHeroMove) {
    var lastX = core.status.automaticRoute.destX,
      lastY = core.status.automaticRoute.destY;
    core.stopAutomaticRoute();
    // 双击瞬移
    if (lastX == destX && lastY == destY) {
      core.status.automaticRoute.moveDirectly = true;
      setTimeout(function () {
        if (
          core.status.automaticRoute.moveDirectly &&
          core.status.heroMoving == 0
        ) {
          core.control.tryMoveDirectly(destX, destY);
        }
        core.status.automaticRoute.moveDirectly = false;
      }, core.values.moveSpeed);
    }
    return true;
  }
  return false;
};

control.prototype._setAutomaticRoute_isTurning = function (
  destX,
  destY,
  stepPostfix
) {
  if (
    destX == core.status.hero.loc.x &&
    destY == core.status.hero.loc.y &&
    stepPostfix.length == 0
  ) {
    if (core.timeout.turnHeroTimeout == null) {
      var routeLength = core.status.route.length;
      core.timeout.turnHeroTimeout = setTimeout(function () {
        if (core.status.route.length == routeLength) core.turnHero();
        clearTimeout(core.timeout.turnHeroTimeout);
        core.timeout.turnHeroTimeout = null;
      }, 250);
    } else {
      clearTimeout(core.timeout.turnHeroTimeout);
      core.timeout.turnHeroTimeout = null;
      core.getNextItem();
    }
    return true;
  }
  if (core.timeout.turnHeroTimeout != null) return true;
  return false;
};

control.prototype._setAutomaticRoute_clickMoveDirectly = function (
  destX,
  destY,
  stepPostfix
) {
  // 单击瞬间移动
  if (core.status.heroStop && core.status.heroMoving == 0) {
    if (
      stepPostfix.length <= 1 &&
      !core.hasFlag("__noClickMove__") &&
      core.control.tryMoveDirectly(destX, destY)
    )
      return true;
  }
  return false;
};

control.prototype._setAutomaticRoute_drawRoute = function (moveStep) {
  // 计算绘制区域的宽高,并尽可能小的创建route层
  var sx = core.bigmap.width * 32,
    sy = core.bigmap.height * 32,
    dx = 0,
    dy = 0;
  moveStep.forEach(function (t) {
    sx = Math.min(sx, t.x * 32);
    dx = Math.max(dx, t.x * 32);
    sy = Math.min(sy, t.y * 32);
    dy = Math.max(dy, t.y * 32);
  });
  core.status.automaticRoute.offsetX = sx;
  core.status.automaticRoute.offsetY = sy;
  var ctx = core.createCanvas(
    "route",
    sx - core.bigmap.offsetX,
    sy - core.bigmap.offsetY,
    dx - sx + 32,
    dy - sy + 32,
    95
  );
  ctx.fillStyle = "#bfbfbf";
  ctx.strokeStyle = "#bfbfbf";
  ctx.lineWidth = 8;
  for (var m = 0; m < moveStep.length; m++) {
    if (m == moveStep.length - 1) {
      ctx.fillRect(
        moveStep[m].x * 32 + 10 - sx,
        moveStep[m].y * 32 + 10 - sy,
        12,
        12
      );
    } else {
      ctx.beginPath();
      var cx = moveStep[m].x * 32 + 16 - sx,
        cy = moveStep[m].y * 32 + 16 - sy;
      var currDir = moveStep[m].direction,
        nextDir = moveStep[m + 1].direction;
      ctx.moveTo(
        cx - core.utils.scan[currDir].x * 11,
        cy - core.utils.scan[currDir].y * 11
      );
      ctx.lineTo(cx, cy);
      ctx.lineTo(
        cx + core.utils.scan[nextDir].x * 11,
        cy + core.utils.scan[nextDir].y * 11
      );
      ctx.stroke();
    }
  }
};

control.prototype._setAutomaticRoute_setAutoSteps = function (moveStep) {
  // 路线转autoStepRoutes
  var step = 0,
    currStep = null;
  moveStep.forEach(function (t) {
    var dir = t.direction;
    if (currStep == null || currStep == dir) step++;
    else {
      core.status.automaticRoute.autoStepRoutes.push({
        direction: currStep,
        step: step,
      });
      step = 1;
    }
    currStep = dir;
  });
  core.status.automaticRoute.autoStepRoutes.push({
    direction: currStep,
    step: step,
  });
};

////// 设置勇士的自动行走路线 //////
control.prototype.setAutoHeroMove = function (steps) {
  steps = steps || core.status.automaticRoute.autoStepRoutes;
  if (steps.length == 0) return;
  core.status.automaticRoute.autoStepRoutes = steps;
  core.status.automaticRoute.autoHeroMove = true;
  core.status.automaticRoute.autoStep = 1;
  core.status.automaticRoute.destStep = steps[0].step;
  core.moveHero(steps[0].direction);
};

////// 设置行走的效果动画 //////
control.prototype.setHeroMoveInterval = function (callback) {
  if (core.status.heroMoving > 0) return;
  if (core.status.replay.speed == 24) {
    if (callback) callback();
    return;
  }

  core.status.heroMoving = 1;

  var toAdd = 1;
  if (core.status.replay.speed > 3) toAdd = 2;
  if (core.status.replay.speed > 6) toAdd = 4;
  if (core.status.replay.speed > 12) toAdd = 8;

  core.interval.heroMoveInterval = window.setInterval(function () {
    core.status.heroMoving += toAdd;
    if (core.status.heroMoving >= 8) {
      clearInterval(core.interval.heroMoveInterval);
      core.status.heroMoving = 0;
      if (callback) callback();
    }
  }, ((core.values.moveSpeed / 8) * toAdd) / core.status.replay.speed);
};

////// 每移动一格后执行的事件 //////
control.prototype.moveOneStep = function (callback) {
  return this.controldata.moveOneStep(callback);
};

////// 实际每一步的行走过程 //////
control.prototype.moveAction = function (callback) {
  if (core.status.heroMoving > 0) return;
  var noPass = core.noPass(core.nextX(), core.nextY()),
    canMove = core.canMoveHero();
  // 下一个点如果不能走
  if (noPass || !canMove) return this._moveAction_noPass(canMove, callback);
  this._moveAction_moving(callback);
};

control.prototype._moveAction_noPass = function (canMove, callback) {
  core.status.route.push(core.getHeroLoc("direction"));
  core.status.automaticRoute.moveStepBeforeStop = [];
  core.status.automaticRoute.lastDirection = core.getHeroLoc("direction");
  if (canMove) core.trigger(core.nextX(), core.nextY());
  core.drawHero();

  if (core.status.automaticRoute.moveStepBeforeStop.length == 0) {
    core.clearContinueAutomaticRoute();
    core.stopAutomaticRoute();
  }
  if (callback) callback();
};

control.prototype._moveAction_moving = function (callback) {
  core.setHeroMoveInterval(function () {
    core.setHeroLoc("x", core.nextX(), true);
    core.setHeroLoc("y", core.nextY(), true);

    var direction = core.getHeroLoc("direction");
    core.control._moveAction_popAutomaticRoute();
    core.status.route.push(direction);

    core.moveOneStep();
    core.checkRouteFolding();
    if (callback) callback();
  });
};

control.prototype._moveAction_popAutomaticRoute = function () {
  var automaticRoute = core.status.automaticRoute;
  // 检查自动寻路是否被弹出
  if (automaticRoute.autoHeroMove) {
    automaticRoute.movedStep++;
    automaticRoute.lastDirection = core.getHeroLoc("direction");
    if (automaticRoute.destStep == automaticRoute.movedStep) {
      if (automaticRoute.autoStep == automaticRoute.autoStepRoutes.length) {
        core.clearContinueAutomaticRoute();
        core.stopAutomaticRoute();
      } else {
        automaticRoute.movedStep = 0;
        automaticRoute.destStep =
          automaticRoute.autoStepRoutes[automaticRoute.autoStep].step;
        core.setHeroLoc(
          "direction",
          automaticRoute.autoStepRoutes[automaticRoute.autoStep].direction
        );
        core.status.automaticRoute.autoStep++;
      }
    }
  }
};

////// 让勇士开始移动 //////
control.prototype.moveHero = function (direction, callback) {
  // 如果正在移动,直接return
  if (core.status.heroMoving != 0) return;
  if (core.isset(direction)) core.setHeroLoc("direction", direction);

  if (callback) return this.moveAction(callback);
  this._moveHero_moving();
};

control.prototype._moveHero_moving = function () {
  // ------ 我已经看不懂这个函数了,反正好用就行23333333
  core.status.heroStop = false;
  core.status.automaticRoute.moveDirectly = false;
  var move = function () {
    if (!core.status.heroStop) {
      if (core.hasFlag("debug") && core.status.ctrlDown) {
        if (core.status.heroMoving != 0) return;
        // 检测是否穿出去
        var nx = core.nextX(),
          ny = core.nextY();
        if (
          nx < 0 ||
          nx >= core.bigmap.width ||
          ny < 0 ||
          ny >= core.bigmap.height
        )
          return;
        core.eventMoveHero(
          [core.getHeroLoc("direction")],
          core.values.moveSpeed,
          move
        );
      } else {
        core.moveAction();
        setTimeout(move, 50);
      }
    }
  };
  move();
};

////// 当前是否正在移动 //////
control.prototype.isMoving = function () {
  return !core.status.heroStop || core.status.heroMoving > 0;
};

////// 停止勇士的一切行动,等待勇士行动结束后,再执行callback //////
control.prototype.waitHeroToStop = function (callback) {
  var lastDirection = core.status.automaticRoute.lastDirection;
  core.stopAutomaticRoute();
  core.clearContinueAutomaticRoute();
  if (callback) {
    core.status.replay.animate = true;
    core.lockControl();
    core.status.automaticRoute.moveDirectly = false;
    setTimeout(
      function () {
        core.status.replay.animate = false;
        if (core.isset(lastDirection))
          core.setHeroLoc("direction", lastDirection);
        core.drawHero();
        callback();
      },
      core.status.replay.speed == 24 ? 1 : 30
    );
  }
};

////// 转向 //////
control.prototype.turnHero = function (direction) {
  if (direction) {
    core.setHeroLoc("direction", direction);
    core.drawHero();
    core.status.route.push("turn:" + direction);
    return;
  }
  core.setHeroLoc("direction", core.turnDirection(":right"));
  core.drawHero();
  core.status.route.push("turn");
  core.checkRouteFolding();
};

////// 瞬间移动 //////
control.prototype.moveDirectly = function (destX, destY, ignoreSteps) {
  return this.controldata.moveDirectly(destX, destY, ignoreSteps);
};

////// 尝试瞬间移动 //////
control.prototype.tryMoveDirectly = function (destX, destY) {
  if (this.nearHero(destX, destY)) return false;
  var canMoveArray = core.maps.generateMovableArray();
  var dirs = [
    [destX, destY],
    [destX - 1, destY, "right"],
    [destX, destY - 1, "down"],
    [destX, destY + 1, "up"],
    [destX + 1, destY, "left"],
  ];
  var canMoveDirectlyArray = core.canMoveDirectlyArray(dirs, canMoveArray);

  for (var i = 0; i < dirs.length; ++i) {
    var d = dirs[i],
      dx = d[0],
      dy = d[1],
      dir = d[2];
    if (dx < 0 || dx >= core.bigmap.width || dy < 0 || dy >= core.bigmap.height)
      continue;
    if (dir && !core.inArray(canMoveArray[dx][dy], dir)) continue;
    if (canMoveDirectlyArray[i] < 0) continue;
    if (core.control.moveDirectly(dx, dy, canMoveDirectlyArray[i])) {
      if (dir) core.moveHero(dir, function () {});
      return true;
    }
  }
  return false;
};

////// 绘制勇士 //////
control.prototype.drawHero = function (status, offset, frame) {
  if (!core.isPlaying() || !core.status.floorId || core.status.gameOver) return;
  var x = core.getHeroLoc("x"),
    y = core.getHeroLoc("y"),
    direction = core.getHeroLoc("direction");
  status = status || "stop";
  if (!offset) offset = 0;

  var way = core.utils.scan2[direction];
  var dx = way.x,
    dy = way.y;
  var offsetX = typeof offset == "number" ? dx * offset : offset.x || 0;
  var offsetY = typeof offset == "number" ? dy * offset : offset.y || 0;
  offset = { x: offsetX, y: offsetY, offset: offset };

  core.clearAutomaticRouteNode(x + dx, y + dy);
  core.clearMap("hero");
  core.status.heroCenter.px = 32 * x + offsetX + 16;
  core.status.heroCenter.py =
    32 * y + offsetY + 32 - core.material.icons.hero.height / 2;

  // 重置hero层画布
  core.setGameCanvasTranslate("hero", 0, 0);
  delete core.canvas.hero._px;
  delete core.canvas.hero._py;
  core.status.preview.enabled = false;
  if (!core.hasFlag("__lockViewport__")) {
    this._drawHero_updateViewport(x, y, offset);
  }

  this._drawHero_draw(direction, x, y, status, offset, frame);
};

control.prototype._drawHero_updateViewport = function (x, y, offset) {
  core.bigmap.offsetX = core.clamp(
    (x - core._HALF_WIDTH_) * 32 + offset.x,
    0,
    Math.max(32 * core.bigmap.width - core._PX_, 0)
  );
  core.bigmap.offsetY = core.clamp(
    (y - core._HALF_HEIGHT_) * 32 + offset.y,
    0,
    Math.max(32 * core.bigmap.height - core._PY_, 0)
  );
  core.control.updateViewport();
};

control.prototype._drawHero_draw = function (
  direction,
  x,
  y,
  status,
  offset,
  frame
) {
  offset = offset || { x: 0, y: 0, offset: 0, px: 0, py: 0 };
  var opacity = core.setAlpha("hero", core.getFlag("__heroOpacity__", 1));
  this._drawHero_getDrawObjs(direction, x, y, status, offset).forEach(function (
    block
  ) {
    core.drawImage(
      "hero",
      block.img,
      ((block.heroIcon[block.status] + (frame || 0)) % 4) * block.width,
      block.heroIcon.loc * block.height,
      block.width,
      block.height,
      block.posx + (32 - block.width) / 2,
      block.posy + 32 - block.height,
      block.width,
      block.height
    );
  });
  core.setAlpha("hero", opacity);
};

control.prototype._drawHero_getDrawObjs = function (
  direction,
  x,
  y,
  status,
  offset
) {
  var heroIconArr = core.material.icons.hero,
    drawObjs = [],
    index = 0;
  drawObjs.push({
    img: core.material.images.hero,
    width: core.material.icons.hero.width || 32,
    height: core.material.icons.hero.height,
    heroIcon: heroIconArr[direction],
    posx: x * 32 - core.bigmap.offsetX + offset.x,
    posy: y * 32 - core.bigmap.offsetY + offset.y,
    status: status,
    index: index++,
  });
  if (typeof offset.offset == "number") {
    core.status.hero.followers.forEach(function (t) {
      drawObjs.push({
        img: core.material.images.images[t.name],
        width: core.material.images.images[t.name].width / 4,
        height: core.material.images.images[t.name].height / 4,
        heroIcon: heroIconArr[t.direction],
        posx:
          32 * t.x -
          core.bigmap.offsetX +
          (t.stop
            ? 0
            : core.utils.scan2[t.direction].x * Math.abs(offset.offset)),
        posy:
          32 * t.y -
          core.bigmap.offsetY +
          (t.stop
            ? 0
            : core.utils.scan2[t.direction].y * Math.abs(offset.offset)),
        status: t.stop ? "stop" : status,
        index: index++,
      });
    });
  }
  return drawObjs.sort(function (a, b) {
    return a.posy == b.posy ? b.index - a.index : a.posy - b.posy;
  });
};

control.prototype.setHeroOpacity = function (
  opacity,
  moveMode,
  time,
  callback
) {
  time = time || 0;
  if (time == 0) {
    core.setFlag("__heroOpacity__", opacity);
    core.drawHero();
    if (callback) callback();
    return;
  }
  time /= Math.max(core.status.replay.speed, 1);

  var fromOpacity = core.getFlag("__heroOpacity__", 1);
  var step = 0,
    steps = parseInt(time / 10);
  if (steps <= 0) steps = 1;
  var moveFunc = core.applyEasing(moveMode);

  var animate = setInterval(function () {
    step++;
    core.setFlag(
      "__heroOpacity__",
      fromOpacity + (opacity - fromOpacity) * moveFunc(step / steps)
    );
    core.drawHero();
    if (step == steps) {
      delete core.animateFrame.asyncId[animate];
      clearInterval(animate);
      if (callback) callback();
    }
  }, 10);

  core.animateFrame.lastAsyncId = animate;
  core.animateFrame.asyncId[animate] = callback;
};

// ------ 画布、位置、阻激夹域,显伤 ------ //

////// 设置画布偏移
control.prototype.setGameCanvasTranslate = function (canvas, x, y) {
  var c = core.dom.gameCanvas[canvas];
  x = x * core.domStyle.scale;
  y = y * core.domStyle.scale;
  c.style.transform = "translate(" + x + "px," + y + "px)";
  c.style.webkitTransform = "translate(" + x + "px," + y + "px)";
  c.style.OTransform = "translate(" + x + "px," + y + "px)";
  c.style.MozTransform = "translate(" + x + "px," + y + "px)";
  if (main.mode === "editor" && editor.isMobile) {
    c.style.transform =
      "translate(" +
      (x / core._PX_) * 96 +
      "vw," +
      (y / core._PY_) * 96 +
      "vw)";
    c.style.webkitTransform =
      "translate(" +
      (x / core._PX_) * 96 +
      "vw," +
      (y / core._PY_) * 96 +
      "vw)";
    c.style.OTransform =
      "translate(" +
      (x / core._PX_) * 96 +
      "vw," +
      (y / core._PY_) * 96 +
      "vw)";
    c.style.MozTransform =
      "translate(" +
      (x / core._PX_) * 96 +
      "vw," +
      (y / core._PY_) * 96 +
      "vw)";
  }
};

////// 加减画布偏移
control.prototype.addGameCanvasTranslate = function (x, y) {
  for (var ii = 0, canvas; (canvas = core.dom.gameCanvas[ii]); ii++) {
    var id = canvas.getAttribute("id");
    if (id == "ui" || id == "data") continue; // UI层和data层不移动
    var offsetX = x,
      offsetY = y;
    if (core.bigmap.canvas.indexOf(id) >= 0) {
      if (core.bigmap.v2) {
        offsetX -= core.bigmap.offsetX - 32 * core.bigmap.posX + 32;
        offsetY -= core.bigmap.offsetY - 32 * core.bigmap.posY + 32;
      } else {
        offsetX -= core.bigmap.offsetX;
        offsetY -= core.bigmap.offsetY;
      }
    }
    core.control.setGameCanvasTranslate(id, offsetX, offsetY);
  }
};

////// 更新视野范围 //////
control.prototype.updateViewport = function () {
  // 当前是否应该重绘?
  if (core.bigmap.v2) {
    if (
      core.bigmap.offsetX >= core.bigmap.posX * 32 + 32 ||
      core.bigmap.offsetX <= core.bigmap.posX * 32 - 32 ||
      core.bigmap.offsetY >= core.bigmap.posY * 32 + 32 ||
      core.bigmap.offsetY <= core.bigmap.posY * 32 - 32
    ) {
      core.bigmap.posX = parseInt(core.bigmap.offsetX / 32);
      core.bigmap.posY = parseInt(core.bigmap.offsetY / 32);
      core.redrawMap();
    }
  } else {
    core.bigmap.posX = core.bigmap.posY = 0;
  }
  var offsetX = core.bigmap.v2
    ? -(core.bigmap.offsetX - 32 * core.bigmap.posX) - 32
    : -core.bigmap.offsetX;
  var offsetY = core.bigmap.v2
    ? -(core.bigmap.offsetY - 32 * core.bigmap.posY) - 32
    : -core.bigmap.offsetY;

  core.bigmap.canvas.forEach(function (cn) {
    core.control.setGameCanvasTranslate(cn, offsetX, offsetY);
  });
  // ------ 路线
  core.relocateCanvas(
    "route",
    core.status.automaticRoute.offsetX - core.bigmap.offsetX,
    core.status.automaticRoute.offsetY - core.bigmap.offsetY
  );
  // ------ 所有的大怪物也都需要重定位
  for (var one in core.dymCanvas) {
    if (one.startsWith("_bigImage_")) {
      var ox = core.dymCanvas[one].canvas.getAttribute("_ox");
      var oy = core.dymCanvas[one].canvas.getAttribute("_oy");
      if (ox != null && oy != null) {
        core.relocateCanvas(
          one,
          parseInt(ox) - core.bigmap.offsetX,
          parseInt(oy) - core.bigmap.offsetY
        );
      }
    }
  }
};

////// 设置视野范围 //////
control.prototype.setViewport = function (px, py) {
  var originOffsetX = core.bigmap.offsetX,
    originOffsetY = core.bigmap.offsetY;
  core.bigmap.offsetX = core.clamp(px, 0, 32 * core.bigmap.width - core._PX_);
  core.bigmap.offsetY = core.clamp(py, 0, 32 * core.bigmap.height - core._PY_);
  this.updateViewport();
  // ------ hero层也需要!
  var px = parseFloat(core.canvas.hero._px) || 0;
  var py = parseFloat(core.canvas.hero._py) || 0;
  px += originOffsetX - core.bigmap.offsetX;
  py += originOffsetY - core.bigmap.offsetY;
  core.control.setGameCanvasTranslate("hero", px, py);
  core.canvas.hero._px = px;
  core.canvas.hero._py = py;
};

////// 移动视野范围 //////
control.prototype.moveViewport = function (x, y, moveMode, time, callback) {
  time = time || 0;
  time /= Math.max(core.status.replay.speed, 1);
  var per_time = 10,
    step = 0,
    steps = parseInt(time / per_time);
  if (steps <= 0) {
    this.setViewport(32 * x, 32 * y);
    if (callback) callback();
    return;
  }
  var px = core.clamp(32 * x, 0, 32 * core.bigmap.width - core._PX_);
  var py = core.clamp(32 * y, 0, 32 * core.bigmap.height - core._PY_);
  var cx = core.bigmap.offsetX;
  var cy = core.bigmap.offsetY;
  var moveFunc = core.applyEasing(moveMode);

  var animate = window.setInterval(function () {
    step++;
    core.setViewport(
      cx + moveFunc(step / steps) * (px - cx),
      cy + moveFunc(step / steps) * (py - cy)
    );
    if (step == steps) {
      delete core.animateFrame.asyncId[animate];
      clearInterval(animate);
      core.setViewport(px, py);
      if (callback) callback();
    }
  }, per_time);

  core.animateFrame.lastAsyncId = animate;
  core.animateFrame.asyncId[animate] = callback;
};

////// 获得勇士面对位置的x坐标 //////
control.prototype.nextX = function (n) {
  if (n == null) n = 1;
  return (
    core.getHeroLoc("x") + core.utils.scan[core.getHeroLoc("direction")].x * n
  );
};

////// 获得勇士面对位置的y坐标 //////
control.prototype.nextY = function (n) {
  if (n == null) n = 1;
  return (
    core.getHeroLoc("y") + core.utils.scan[core.getHeroLoc("direction")].y * n
  );
};

////// 某个点是否在勇士旁边 //////
control.prototype.nearHero = function (x, y, n) {
  if (n == null) n = 1;
  return (
    Math.abs(x - core.getHeroLoc("x")) + Math.abs(y - core.getHeroLoc("y")) <= n
  );
};

////// 聚集跟随者 //////
control.prototype.gatherFollowers = function () {
  var x = core.getHeroLoc("x"),
    y = core.getHeroLoc("y"),
    dir = core.getHeroLoc("direction");
  core.status.hero.followers.forEach(function (t) {
    t.x = x;
    t.y = y;
    t.stop = true;
    t.direction = dir;
  });
};

////// 更新跟随者坐标 //////
control.prototype.updateFollowers = function () {
  core.status.hero.followers.forEach(function (t) {
    if (!t.stop) {
      t.x += core.utils.scan2[t.direction].x;
      t.y += core.utils.scan2[t.direction].y;
    }
  });

  var nowx = core.getHeroLoc("x"),
    nowy = core.getHeroLoc("y");
  core.status.hero.followers.forEach(function (t) {
    t.stop = true;
    var dx = nowx - t.x,
      dy = nowy - t.y;
    for (var dir in core.utils.scan2) {
      if (core.utils.scan2[dir].x == dx && core.utils.scan2[dir].y == dy) {
        t.stop = false;
        t.direction = dir;
      }
    }
    nowx = t.x;
    nowy = t.y;
  });
};

////// 瞬移更新跟随者坐标 //////
control.prototype._moveDirectyFollowers = function (x, y) {
  var route = core.automaticRoute(x, y);
  if (route.length == 0)
    route = [{ x: x, y: y, direction: core.getHeroLoc("direction") }];

  var nowx = x,
    nowy = y;
  for (var i = 0; i < core.status.hero.followers.length; ++i) {
    var t = core.status.hero.followers[i];
    var index = route.length - i - 2;
    if (index < 0) index = 0;
    t.stop = true;
    t.x = route[index].x;
    t.y = route[index].y;
    t.direction = route[index].direction;
    var dx = nowx - t.x,
      dy = nowy - t.y;
    for (var dir in core.utils.scan2) {
      if (core.utils.scan2[dir].x == dx && core.utils.scan2[dir].y == dy) {
        t.stop = false;
        t.direction = dir;
      }
    }
    nowx = t.x;
    nowy = t.y;
  }
};

////// 更新领域、夹击、阻击的伤害地图 //////
control.prototype.updateCheckBlock = function (floorId) {
  return this.controldata.updateCheckBlock(floorId);
};

////// 检查并执行领域、夹击、阻击事件 //////
control.prototype.checkBlock = function () {
  var x = core.getHeroLoc("x"),
    y = core.getHeroLoc("y"),
    loc = x + "," + y;
  var damage = core.status.checkBlock.damage[loc];
  if (damage) {
    core.status.hero.hp -= damage;
    var text =
      Object.keys(core.status.checkBlock.type[loc] || {}).join(",") || "伤害";
    core.drawTip("受到" + text + damage + "点");
    core.drawHeroAnimate("zone");
    this._checkBlock_disableQuickShop();
    core.status.hero.statistics.extraDamage += damage;
    if (core.status.hero.hp <= 0) {
      core.status.hero.hp = 0;
      core.updateStatusBar(false, true);
      core.events.lose();
      return;
    } else {
      core.updateStatusBar(false, true);
    }
  }
  this._checkBlock_ambush(core.status.checkBlock.ambush[loc]);
  this._checkBlock_repulse(core.status.checkBlock.repulse[loc]);
};

control.prototype._checkBlock_disableQuickShop = function () {
  // 禁用快捷商店
  if (core.flags.disableShopOnDamage) {
    Object.keys(core.status.shops).forEach(function (shopId) {
      core.setShopVisited(shopId, false);
    });
  }
};

////// 阻击 //////
control.prototype._checkBlock_repulse = function (repulse) {
  if (!repulse || repulse.length == 0) return;
  var actions = [];
  repulse.forEach(function (t) {
    actions.push({
      type: "move",
      loc: [t[0], t[1]],
      steps: [t[3]],
      time: 250,
      keep: true,
      async: true,
    });
  });
  actions.push({ type: "waitAsync" });
  core.insertAction(actions);
};

////// 捕捉 //////
control.prototype._checkBlock_ambush = function (ambush) {
  if (!ambush || ambush.length == 0) return;
  // 捕捉效果
  var actions = [];
  ambush.forEach(function (t) {
    actions.push({
      type: "move",
      loc: [t[0], t[1]],
      steps: [t[3]],
      time: 250,
      keep: false,
      async: true,
    });
  });
  actions.push({ type: "waitAsync" });
  // 强制战斗
  ambush.forEach(function (t) {
    actions.push({
      type: "function",
      function:
        "function() { " +
        "core.battle('" +
        t[2] +
        "', " +
        t[0] +
        "," +
        t[1] +
        ", true, core.doAction); " +
        "}",
      async: true,
    });
  });
  core.insertAction(actions);
};

////// 更新全地图显伤 //////
control.prototype.updateDamage = function (floorId, ctx) {
  floorId = floorId || core.status.floorId;
  if (!floorId || core.status.gameOver || main.mode != "play") return;
  var onMap = ctx == null;

  // 没有怪物手册
  if (!core.hasItem("book")) return;
  core.status.damage.posX = core.bigmap.posX;
  core.status.damage.posY = core.bigmap.posY;
  if (!onMap) {
    var width = core.floors[floorId].width,
      height = core.floors[floorId].height;
    // 地图过大的缩略图不绘制显伤
    if (width * height > core.bigmap.threshold) return;
  }
  this._updateDamage_damage(floorId, onMap);
  this._updateDamage_extraDamage(floorId, onMap);
  this.drawDamage(ctx);
};

control.prototype._updateDamage_damage = function (floorId, onMap) {
  core.status.damage.data = [];
  if (!core.flags.displayEnemyDamage && !core.flags.displayExtraDamage) return;

  core.extractBlocks(floorId);
  core.status.maps[floorId].blocks.forEach(function (block) {
    var x = block.x,
      y = block.y;

    // v2优化,只绘制范围内的部分
    if (onMap && core.bigmap.v2) {
      if (
        x < core.bigmap.posX - core.bigmap.extend ||
        x > core.bigmap.posX + core._WIDTH_ + core.bigmap.extend ||
        y < core.bigmap.posY - core.bigmap.extend ||
        y > core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend
      ) {
        return;
      }
    }

    if (
      !block.disable &&
      block.event.cls.indexOf("enemy") == 0 &&
      block.event.displayDamage !== false
    ) {
      if (core.flags.displayEnemyDamage) {
        var damageString = core.enemys.getDamageString(
          block.event.id,
          x,
          y,
          floorId
        );
        core.status.damage.data.push({
          text: damageString.damage,
          px: 32 * x + 1,
          py: 32 * (y + 1) - 1,
          color: damageString.color,
        });
      }
      if (core.flags.displayCritical) {
        var critical = core.enemys.nextCriticals(
          block.event.id,
          1,
          x,
          y,
          floorId
        );
        critical = core.formatBigNumber((critical[0] || [])[0], true);
        if (critical == "???") critical = "?";
        core.status.damage.data.push({
          text: critical,
          px: 32 * x + 1,
          py: 32 * (y + 1) - 11,
          color: "#FFFFFF",
        });
      }
    }
  });
};

control.prototype._updateDamage_extraDamage = function (floorId, onMap) {
  core.status.damage.extraData = [];
  if (!core.flags.displayExtraDamage) return;

  var width = core.floors[floorId].width,
    height = core.floors[floorId].height;
  var startX =
    onMap && core.bigmap.v2
      ? Math.max(0, core.bigmap.posX - core.bigmap.extend)
      : 0;
  var endX =
    onMap && core.bigmap.v2
      ? Math.min(
          width,
          core.bigmap.posX + core._WIDTH_ + core.bigmap.extend + 1
        )
      : width;
  var startY =
    onMap && core.bigmap.v2
      ? Math.max(0, core.bigmap.posY - core.bigmap.extend)
      : 0;
  var endY =
    onMap && core.bigmap.v2
      ? Math.min(
          height,
          core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend + 1
        )
      : height;

  for (var x = startX; x < endX; x++) {
    for (var y = startY; y < endY; y++) {
      var alpha = 1;
      if (core.noPass(x, y, floorId)) {
        if (core.flags.extraDamageType == 2) alpha = 0;
        else if (core.flags.extraDamageType == 1) alpha = 0.6;
      }
      var damage = core.status.checkBlock.damage[x + "," + y] || 0;
      if (damage > 0) {
        // 该点伤害
        damage = core.formatBigNumber(damage, true);
        core.status.damage.extraData.push({
          text: damage,
          px: 32 * x + 16,
          py: 32 * (y + 1) - 14,
          color: "#ffaa33",
          alpha: alpha,
        });
      } else {
        // 检查捕捉
        if (core.status.checkBlock.ambush[x + "," + y]) {
          core.status.damage.extraData.push({
            text: "!",
            px: 32 * x + 16,
            py: 32 * (y + 1) - 14,
            color: "#ffaa33",
            alpha: alpha,
          });
        }
      }
    }
  }
};

////// 重绘地图显伤 //////
control.prototype.drawDamage = function (ctx) {
  if (core.status.gameOver || !core.status.damage || main.mode != "play")
    return;
  var onMap = false;
  if (ctx == null) {
    ctx = core.canvas.damage;
    core.clearMap("damage");
    onMap = true;
  }

  if (onMap && core.bigmap.v2) {
    // 检查是否需要重算...
    if (
      Math.abs(core.bigmap.posX - core.status.damage.posX) >=
        core.bigmap.extend - 1 ||
      Math.abs(core.bigmap.posY - core.status.damage.posY) >=
        core.bigmap.extend - 1
    ) {
      return this.updateDamage();
    }
  }
  return this._drawDamage_draw(ctx, onMap);
};

control.prototype._drawDamage_draw = function (ctx, onMap) {
  if (!core.hasItem("book")) return;

  core.setFont(ctx, "bold 11px Arial");
  core.setTextAlign(ctx, "left");
  core.status.damage.data.forEach(function (one) {
    var px = one.px,
      py = one.py;
    if (onMap && core.bigmap.v2) {
      px -= core.bigmap.posX * 32;
      py -= core.bigmap.posY * 32;
      if (
        px < -32 * 2 ||
        px > core._PX_ + 32 ||
        py < -32 ||
        py > core._PY_ + 32
      )
        return;
    }
    core.fillBoldText(ctx, one.text, px, py, one.color);
  });

  core.setTextAlign(ctx, "center");
  core.status.damage.extraData.forEach(function (one) {
    var px = one.px,
      py = one.py;
    if (onMap && core.bigmap.v2) {
      px -= core.bigmap.posX * 32;
      py -= core.bigmap.posY * 32;
      if (px < -32 || px > core._PX_ + 32 || py < -32 || py > core._PY_ + 32)
        return;
    }
    var alpha = core.setAlpha(ctx, one.alpha);
    core.fillBoldText(ctx, one.text, px, py, one.color);
    core.setAlpha(ctx, alpha);
  });
};

// ------ 录像相关 ------ //

////// 选择录像文件 //////
control.prototype.chooseReplayFile = function () {
  core.readFile(
    function (obj) {
      if (obj.name != core.firstData.name) return alert("存档和游戏不一致!");
      if (!obj.route) return alert("无效的录像!");
      var _replay = function () {
        core.startGame(
          core.flags.startUsingCanvas ? "" : obj.hard || "",
          obj.seed,
          core.decodeRoute(obj.route)
        );
      };
      if (obj.version && obj.version != core.firstData.version) {
        core.myconfirm("游戏版本不一致!\n你仍然想播放录像吗?", _replay);
        return;
      }
      _replay();
    },
    null,
    ".h5route"
  );
};

////// 开始播放 //////
control.prototype.startReplay = function (list) {
  if (!core.isPlaying()) return;
  core.status.replay.replaying = true;
  core.status.replay.pausing = true;
  core.status.replay.failed = false;
  core.status.replay.speed = 1.0;
  core.status.replay.toReplay = core.cloneArray(list);
  core.status.replay.totalList = core.status.route.concat(list);
  core.status.replay.steps = 0;
  core.status.replay.save = [];
  core.createCanvas("replay", 0, core._PY_ - 40, core._PX_, 40, 199);
  core.setOpacity("replay", 0.6);
  this._replay_drawProgress();
  core.updateStatusBar(false, true);
  core.drawTip("开始播放");
  this.replay();
};

////// 更改播放状态 //////
control.prototype.triggerReplay = function () {
  if (core.status.replay.pausing) this.resumeReplay();
  else this.pauseReplay();
};

////// 暂停播放 //////
control.prototype.pauseReplay = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  core.status.replay.pausing = true;
  core.updateStatusBar(false, true);
  core.drawTip("暂停播放");
};

////// 恢复播放 //////
control.prototype.resumeReplay = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  core.status.replay.pausing = false;
  core.updateStatusBar(false, true);
  core.drawTip("恢复播放");
  core.replay();
};

////// 单步播放 //////
control.prototype.stepReplay = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (!core.status.replay.pausing) {
    core.playSound("操作失败");
    return core.drawTip("请先暂停录像");
  }
  if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  core.replay(true);
};

////// 加速播放 //////
control.prototype.speedUpReplay = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24];
  for (var i = speeds.length - 2; i >= 0; i--) {
    if (speeds[i] <= core.status.replay.speed) {
      core.status.replay.speed = speeds[i + 1];
      break;
    }
  }
  core.drawTip("x" + core.status.replay.speed + "倍");
};

////// 减速播放 //////
control.prototype.speedDownReplay = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24];
  for (var i = 1; i <= speeds.length; i++) {
    if (speeds[i] >= core.status.replay.speed) {
      core.status.replay.speed = speeds[i - 1];
      break;
    }
  }
  core.drawTip("x" + core.status.replay.speed + "倍");
};

////// 设置播放速度 //////
control.prototype.setReplaySpeed = function (speed) {
  if (!core.isPlaying() || !core.isReplaying()) return;
  core.status.replay.speed = speed;
  core.drawTip("x" + core.status.replay.speed + "倍");
};

////// 停止播放 //////
control.prototype.stopReplay = function (force) {
  if (!core.isPlaying()) return;
  if (!core.isReplaying() && !force) return;
  core.status.replay.toReplay = [];
  core.status.replay.totalList = [];
  core.status.replay.replaying = false;
  core.status.replay.pausing = false;
  core.status.replay.failed = false;
  core.status.replay.speed = 1.0;
  core.status.replay.steps = 0;
  core.status.replay.save = [];
  core.deleteCanvas("replay");
  core.updateStatusBar(false, true);
  core.drawTip("停止播放并恢复游戏");
};

////// 回退 //////
control.prototype.rewindReplay = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (!core.status.replay.pausing) {
    core.playSound("操作失败");
    return core.drawTip("请先暂停录像");
  }
  if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  if (core.status.replay.save.length == 0) {
    core.playSound("操作失败");
    return core.drawTip("无法再回到上一个节点");
  }
  var save = core.status.replay.save,
    data = save.pop();
  core.loadData(data.data, function () {
    core.removeFlag("__fromLoad__");
    core.status.replay = {
      replaying: true,
      pausing: true,
      animate: false,
      toReplay: data.replay.toReplay,
      totalList: data.replay.totalList,
      speed: core.status.replay.speed,
      steps: data.replay.steps,
      save: save,
    };
    core.createCanvas("replay", 0, core._PY_ - 40, core._PX_, 40, 199);
    core.setOpacity("replay", 0.6);
    core.control._replay_drawProgress();
    core.updateStatusBar(false, true);
    core.drawTip("成功回退到上一个节点");
  });
};

////// 回放时存档 //////
control.prototype._replay_SL = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (!core.status.replay.pausing) {
    core.playSound("操作失败");
    return core.drawTip("请先暂停录像");
  }
  if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  if (core.hasFlag("__forbidSave__")) {
    core.playSound("操作失败");
    return core.drawTip("当前禁止存档");
  }
  this._replay_hideProgress();

  core.lockControl();
  core.status.event.id = "save";
  var saveIndex = core.saves.saveIndex;
  var page = parseInt((saveIndex - 1) / 5),
    offset = saveIndex - 5 * page;

  core.ui._drawSLPanel(10 * page + offset);
};

////// 回放时查看怪物手册 //////
control.prototype._replay_book = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (!core.status.replay.pausing) {
    core.playSound("操作失败");
    return core.drawTip("请先暂停录像");
  }
  if (
    core.isMoving() ||
    core.status.replay.animate ||
    (core.status.event.id && core.status.event.id != "viewMaps")
  ) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  if (!core.hasItem("book")) {
    core.playSound("操作失败");
    return core.drawTip("你没有" + core.material.items["book"].name, "book");
  }
  this._replay_hideProgress();

  // 从“浏览地图”页面打开
  if (core.status.event.id == "viewMaps")
    core.status.event.ui = core.status.event.data;

  core.lockControl();
  core.status.event.id = "book";
  core.useItem("book", true);
};

////// 回放录像时浏览地图 //////
control.prototype._replay_viewMap = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (!core.status.replay.pausing) {
    core.playSound("操作失败");
    return core.drawTip("请先暂停录像");
  }
  if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  this._replay_hideProgress();

  core.lockControl();
  core.status.event.id = "viewMaps";
  core.ui._drawViewMaps();
};

control.prototype._replay_toolbox = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (!core.status.replay.pausing) {
    core.playSound("操作失败");
    return core.drawTip("请先暂停录像");
  }
  if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  this._replay_hideProgress();

  core.lockControl();
  core.status.event.id = "toolbox";
  core.ui._drawToolbox();
};

control.prototype._replay_equipbox = function () {
  if (!core.isPlaying() || !core.isReplaying()) return;
  if (!core.status.replay.pausing) {
    core.playSound("操作失败");
    return core.drawTip("请先暂停录像");
  }
  if (core.isMoving() || core.status.replay.animate || core.status.event.id) {
    core.playSound("操作失败");
    return core.drawTip("请等待当前事件的处理结束");
  }
  this._replay_hideProgress();

  core.lockControl();
  core.status.event.id = "equipbox";
  core.ui._drawEquipbox();
};

////// 是否正在播放录像 //////
control.prototype.isReplaying = function () {
  return (core.status.replay || {}).replaying;
};

////// 回放 //////
control.prototype.replay = function (force) {
  if (
    !core.isPlaying() ||
    !core.isReplaying() ||
    core.status.replay.animate ||
    core.status.event.id ||
    core.status.replay.failed
  )
    return;
  if (core.status.replay.pausing && !force) return;
  this._replay_drawProgress();
  if (core.status.replay.toReplay.length == 0) return this._replay_finished();
  this._replay_save();
  var action = core.status.replay.toReplay.shift();
  if (this._doReplayAction(action)) return;
  this._replay_error(action);
};

////// 注册一个录像行为 //////
// name:自定义名称,可用于注销使用
// func:具体执行录像的函数,可为一个函数或插件中的函数名;
//       需要接受一个action参数,代表录像回放时的下一个操作
// func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。
control.prototype.registerReplayAction = function (name, func) {
  this.unregisterReplayAction(name);
  this.replayActions.push({ name: name, func: func });
};

////// 注销一个录像行为 //////
control.prototype.unregisterReplayAction = function (name) {
  this.replayActions = this.replayActions.filter(function (b) {
    return b.name != name;
  });
};

////// 执行录像行为,会在注册的函数中依次执行直到得到true为止 //////
control.prototype._doReplayAction = function (action) {
  for (var i in this.replayActions) {
    try {
      if (core.doFunc(this.replayActions[i].func, this, action)) return true;
    } catch (e) {
      console.error(e);
      console.error(
        "ERROR in replayActions[" +
          this.replayActions[i].name +
          "]:已自动注销该项。"
      );
      core.unregisterReplayAction(this.replayActions[i].name);
    }
  }
  return false;
};

control.prototype._replay_finished = function () {
  core.status.replay.replaying = false;
  core.status.replay.failed = false;
  core.status.event.selection = 0;
  var str = "录像播放完毕,你想退出播放吗?";
  if (
    core.status.route.length != core.status.replay.totalList.length ||
    core.subarray(core.status.route, core.status.replay.totalList) == null
  ) {
    str =
      "录像播放完毕,但记录不一致。\n请检查录像播放时的二次记录问题。\n你想退出播放吗?";
  }
  core.ui.drawConfirmBox(
    str,
    function () {
      core.ui.closePanel();
      core.stopReplay(true);
    },
    function () {
      core.status.replay.replaying = true;
      core.ui.closePanel();
      core.pauseReplay();
    }
  );
};

control.prototype._replay_save = function () {
  core.status.replay.steps++;
  if (core.status.replay.steps % 40 == 1) {
    if (core.status.replay.save.length == 30) core.status.replay.save.shift();
    core.status.replay.save.push({
      data: core.saveData(),
      replay: {
        totalList: core.cloneArray(core.status.replay.totalList),
        toReplay: core.cloneArray(core.status.replay.toReplay),
        steps: core.status.replay.steps,
      },
    });
  }
};

control.prototype._replay_error = function (action, callback) {
  core.ui.closePanel();
  core.status.replay.replaying = false;
  core.status.replay.failed = true;
  var len = core.status.replay.toReplay.length;
  var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1);
  var nextList = core.status.replay.toReplay.slice(0, 10);
  console.log("录像文件出错,当前操作:" + action);
  console.log("之前的10个操作是:\n" + prevList.toString());
  console.log("接下来10个操作是:\n" + nextList.toString());
  core.ui.drawConfirmBox(
    "录像文件出错,你想回到上个节点吗?",
    function () {
      core.status.replay.failed = false;
      core.ui.closePanel();
      if (core.status.replay.save.length > 0) {
        core.status.replay.replaying = true;
        core.status.replay.pausing = true;
        core.rewindReplay();
      } else {
        core.playSound("操作失败");
        core.stopReplay(true);
        core.drawTip("无法回到上一个节点");
        if (callback) callback();
      }
    },
    function () {
      core.status.replay.failed = false;
      core.ui.closePanel();
      core.stopReplay(true);
      if (callback) callback();
    }
  );
};

control.prototype._replay_hideProgress = function () {
  if (core.dymCanvas.replay)
    core.dymCanvas.replay.canvas.style.display = "none";
};

control.prototype._replay_drawProgress = function () {
  if (!core.dymCanvas.replay) return;
  if (core.dymCanvas.replay.canvas.style.display == "none")
    core.dymCanvas.replay.canvas.style.display = "block";
  var total = core.status.replay.totalList.length,
    left = total - core.status.replay.toReplay.length;
  var content =
    "播放进度:" +
    left +
    " / " +
    total +
    "(" +
    ((left / total) * 100).toFixed(2) +
    "%)";
  var width = 26 + core.calWidth("replay", content, "16px Arial");
  core.clearMap("replay");
  core.fillRect("replay", 0, 0, width, 40, "#000000");
  core.fillText("replay", content, 16, 27, "#FFFFFF");
};

control.prototype.__replay_getTimeout = function () {
  if (core.status.replay.speed == 24) return 0;
  return 750 / Math.max(1, core.status.replay.speed);
};

control.prototype._replayAction_move = function (action) {
  if (["up", "down", "left", "right"].indexOf(action) < 0) return false;
  core.moveHero(action, core.replay);
  return true;
};

control.prototype._replayAction_item = function (action) {
  if (action.indexOf("item:") != 0) return false;
  var itemId = action.substring(5);
  if (!core.canUseItem(itemId)) return false;
  if (
    core.material.items[itemId].hideInReplay ||
    core.status.replay.speed == 24
  ) {
    core.useItem(itemId, false, core.replay);
    return true;
  }
  var tools = core.getToolboxItems("tools"),
    constants = core.getToolboxItems("constants");
  var index,
    per = core._WIDTH_ - 1;
  if ((index = tools.indexOf(itemId)) >= 0) {
    core.status.event.data = {
      toolsPage: Math.floor(index / per) + 1,
      constantsPage: 1,
    };
    index = index % per;
  } else if ((index = constants.indexOf(itemId)) >= 0) {
    core.status.event.data = {
      toolsPage: 1,
      constantsPage: Math.floor(index / per) + 1,
    };
    index = (index % per) + per;
  }
  if (index < 0) return false;
  core.ui._drawToolbox(index);
  setTimeout(function () {
    core.ui.closePanel();
    core.useItem(itemId, false, core.replay);
  }, core.control.__replay_getTimeout());
  return true;
};

control.prototype._replayAction_equip = function (action) {
  if (action.indexOf("equip:") != 0) return false;
  var equipId = action.substring(6);
  var ownEquipment = core.getToolboxItems("equips");
  var index = ownEquipment.indexOf(equipId),
    per = core._WIDTH_ - 1;
  if (index < 0) {
    core.removeFlag("__doNotCheckAutoEvents__");
    return false;
  }

  var cb = function () {
    var next = core.status.replay.toReplay[0] || "";
    if (!next.startsWith("equip:") && !next.startsWith("unEquip:")) {
      core.removeFlag("__doNotCheckAutoEvents__");
      core.checkAutoEvents();
    }
    core.replay();
  };
  core.setFlag("__doNotCheckAutoEvents__", true);

  core.status.route.push(action);
  if (
    core.material.items[equipId].hideInReplay ||
    core.status.replay.speed == 24
  ) {
    core.loadEquip(equipId, cb);
    return true;
  }
  core.status.event.data = {
    page: Math.floor(index / per) + 1,
    selectId: null,
  };
  index = (index % per) + per;
  core.ui._drawEquipbox(index);
  setTimeout(function () {
    core.ui.closePanel();
    core.loadEquip(equipId, cb);
  }, core.control.__replay_getTimeout());
  return true;
};

control.prototype._replayAction_unEquip = function (action) {
  if (action.indexOf("unEquip:") != 0) return false;
  var equipType = parseInt(action.substring(8));
  if (!core.isset(equipType)) {
    core.removeFlag("__doNotCheckAutoEvents__");
    return false;
  }

  var cb = function () {
    var next = core.status.replay.toReplay[0] || "";
    if (!next.startsWith("equip:") && !next.startsWith("unEquip:")) {
      core.removeFlag("__doNotCheckAutoEvents__");
      core.checkAutoEvents();
    }
    core.replay();
  };
  core.setFlag("__doNotCheckAutoEvents__", true);

  core.ui._drawEquipbox(equipType);
  core.status.route.push(action);
  if (core.status.replay.speed == 24) {
    core.unloadEquip(equipType, cb);
    return true;
  }
  setTimeout(function () {
    core.ui.closePanel();
    core.unloadEquip(equipType, cb);
  }, core.control.__replay_getTimeout());
  return true;
};

control.prototype._replayAction_saveEquip = function (action) {
  if (action.indexOf("saveEquip:") != 0) return false;
  core.quickSaveEquip(parseInt(action.substring(10)));
  core.replay();
  return true;
};

control.prototype._replayAction_loadEquip = function (action) {
  if (action.indexOf("loadEquip:") != 0) return false;
  core.quickLoadEquip(parseInt(action.substring(10)));
  core.replay();
  return true;
};

control.prototype._replayAction_fly = function (action) {
  if (action.indexOf("fly:") != 0) return false;
  var floorId = action.substring(4);
  var toIndex = core.floorIds.indexOf(floorId);
  if (
    !core.canUseItem("fly") ||
    (core.flags.flyNearStair && !core.nearStair() && !flags.canMoveFloor)
  )
    return false;
  core.ui.drawFly(toIndex);
  if (core.status.replay.speed == 24) {
    if (!core.flyTo(floorId, core.replay)) core.control._replay_error(action);
    return true;
  }
  setTimeout(function () {
    if (!core.flyTo(floorId, core.replay)) core.control._replay_error(action);
  }, core.control.__replay_getTimeout());
  return true;
};

control.prototype._replayAction_shop = function (action) {
  if (action.indexOf("shop:") != 0) return false;
  var shopId = action.substring(5);
  if (core.canUseQuickShop(shopId) != null || !core.canOpenShop(shopId)) {
    this._replay_error(shopId);
    return true;
  }
  core.openShop(shopId, false);
  core.replay();
  return true;
};

control.prototype._replayAction_turn = function (action) {
  if (action != "turn" && action.indexOf("turn:") != 0) return false;
  if (action == "turn") core.turnHero();
  else core.turnHero(action.substring(5));
  core.replay();
  return true;
};

control.prototype._replayAction_getNext = function (action) {
  if (action != "getNext") return false;
  core.getNextItem();
  core.replay();
  return true;
};

control.prototype._replayAction_moveDirectly = function (action) {
  if (action.indexOf("move:") != 0) return false;
  // 忽略连续的瞬移事件;如果大地图某一边超过计算范围则不合并
  if (
    !core.hasFlag("poison") &&
    core.status.thisMap.width < 2 * core.bigmap.extend + core._WIDTH_ &&
    core.status.thisMap.height < 2 * core.bigmap.extend + core._HEIGHT_
  ) {
    while (
      core.status.replay.toReplay.length > 0 &&
      core.status.replay.toReplay[0].indexOf("move:") == 0
    ) {
      core.status.route.push(action);
      action = core.status.replay.toReplay.shift();
    }
  }

  var pos = action.substring(5).split(":");
  var x = parseInt(pos[0]),
    y = parseInt(pos[1]);
  var nowx = core.getHeroLoc("x"),
    nowy = core.getHeroLoc("y");
  var ignoreSteps = core.canMoveDirectly(x, y);
  if (!core.moveDirectly(x, y, ignoreSteps)) return false;
  if (core.status.replay.speed == 24) {
    core.replay();
    return true;
  }

  core.ui.drawArrow(
    "ui",
    32 * nowx + 16 - core.bigmap.offsetX,
    32 * nowy + 16 - core.bigmap.offsetY,
    32 * x + 16 - core.bigmap.offsetX,
    32 * y + 16 - core.bigmap.offsetY,
    "#FF0000",
    3
  );
  var timeout = this.__replay_getTimeout();
  if (ignoreSteps < 10) timeout = (timeout * ignoreSteps) / 10;
  setTimeout(function () {
    core.clearMap("ui");
    core.replay();
  }, timeout);
  return true;
};

control.prototype._replayAction_key = function (action) {
  if (action.indexOf("key:") != 0) return false;
  core.actions.keyUp(parseInt(action.substring(4)), false, true);
  core.replay();
  return true;
};

control.prototype._replayAction_click = function (action) {
  if (action.indexOf("click:") != 0) return false;
  var p = action.split(":");
  if (p.length != 4) return false;
  core.actions.doRegisteredAction(
    "onStatusBarClick",
    parseInt(p[2]),
    parseInt(p[3]),
    parseInt(p[1])
  );
  core.replay();
  return true;
};

control.prototype._replayAction_ignoreInput = function (action) {
  if (
    action.indexOf("input:") == 0 ||
    action.indexOf("input2:") == 0 ||
    action.indexOf("choices:") == 0 ||
    action.indexOf("random:") == 0
  ) {
    console.warn("警告!录像播放中出现了未知的 " + action + "!");
    core.replay();
    return true;
  }
  return false;
};

control.prototype._replayAction_no = function (action) {
  if (action != "no") return false;
  core.status.route.push(action);
  core.replay();
  return true;
};

// ------ 存读档相关 ------ //

////// 自动存档 //////
control.prototype.autosave = function (removeLast) {
  if (core.hasFlag("__forbidSave__")) return;
  var x = null;
  if (removeLast) {
    x = core.status.route.pop();
    core.status.route.push("turn:" + core.getHeroLoc("direction"));
  }
  if (core.status.event.id == "action" && !removeLast)
    // 事件中自动存档,读档后是否回到事件触发前
    core.setFlag("__events__", core.clone(core.status.event.data));
  if (core.saves.autosave.data == null) {
    core.saves.autosave.data = [];
  }
  core.saves.autosave.data.splice(core.saves.autosave.now, 0, core.saveData());
  core.saves.autosave.now += 1;
  if (core.saves.autosave.data.length > core.saves.autosave.max) {
    if (core.saves.autosave.now < core.saves.autosave.max / 2)
      core.saves.autosave.data.pop();
    else {
      core.saves.autosave.data.shift();
      core.saves.autosave.now = core.saves.autosave.now - 1;
    }
  }
  core.saves.autosave.updated = true;
  core.saves.ids[0] = true;
  core.removeFlag("__events__");
  if (removeLast) {
    core.status.route.pop();
    if (x) core.status.route.push(x);
  }
};

/////// 实际进行自动存档 //////
control.prototype.checkAutosave = function () {
  if (!core.animateFrame || !core.saves || !core.saves.autosave) return;
  core.setLocalStorage("totalTime", core.animateFrame.totalTime);
  var autosave = core.saves.autosave;
  if (autosave.data == null || !autosave.updated || !autosave.storage) return;
  autosave.updated = false;
  if (autosave.data.length >= 1) {
    core.setLocalForage("autoSave", autosave.data[autosave.now - 1]);
  }
};

////// 实际进行存读档事件 //////
control.prototype.doSL = function (id, type) {
  switch (type) {
    case "save":
      this._doSL_save(id);
      break;
    case "load":
      this._doSL_load(id, this._doSL_load_afterGet);
      break;
    case "reload":
      this._doSL_reload(id, this._doSL_load_afterGet);
      break;
    case "replayLoad":
      this._doSL_load(id, this._doSL_replayLoad_afterGet);
      break;
    case "replayRemain":
      this._doSL_load(id, this._doSL_replayRemain_afterGet);
      break;
    case "replaySince":
      this._doSL_load(id, this._doSL_replaySince_afterGet);
      break;
  }
};

control.prototype._doSL_save = function (id) {
  if (id == "autoSave") {
    core.playSound("操作失败");
    return core.drawTip("不能覆盖自动存档!");
  }
  // 在事件中的存档
  if (core.status.event.interval != null)
    core.setFlag("__events__", core.status.event.interval);
  var data = core.saveData();
  if (core.isReplaying() && core.status.replay.toReplay.length > 0) {
    data.__toReplay__ = core.encodeRoute(core.status.replay.toReplay);
  }
  core.setLocalForage(
    "save" + id,
    data,
    function () {
      core.saves.saveIndex = id;
      core.setLocalStorage("saveIndex", core.saves.saveIndex);
      // 恢复事件
      if (!core.events.recoverEvents(core.status.event.interval))
        core.ui.closePanel();
      core.playSound("存档");
      core.drawTip("存档成功!");
    },
    function (err) {
      console.error(err);
      alert("存档失败,错误信息:\n" + err);
    }
  );
  core.removeFlag("__events__");
  return;
};

control.prototype._doSL_load = function (id, callback) {
  if (id == "autoSave" && core.saves.autosave.data != null) {
    core.saves.autosave.now -= 1;
    var data = core.saves.autosave.data.splice(core.saves.autosave.now, 1)[0];
    if (core.isPlaying() && !core.status.gameOver) {
      core.control.autosave(0);
      core.saves.autosave.now -= 1;
    }
    if (core.saves.autosave.now == 0) {
      core.saves.autosave.data.unshift(core.clone(data));
      core.saves.autosave.now += 1;
    }
    callback(id, data);
  } else {
    core.getLocalForage(
      id == "autoSave" ? id : "save" + id,
      null,
      function (data) {
        if (id == "autoSave" && data != null) {
          core.saves.autosave.data = data;
          if (!(core.saves.autosave.data instanceof Array)) {
            core.saves.autosave.data = [core.saves.autosave.data];
          }
          core.saves.autosave.now = core.saves.autosave.data.length;
          return core.control._doSL_load(id, callback);
        }
        callback(id, data);
      },
      function (err) {
        console.error(err);
        alert("无效的存档");
      }
    );
  }
  return;
};

control.prototype._doSL_reload = function (id, callback) {
  if (
    core.saves.autosave.data != null &&
    core.saves.autosave.now < core.saves.autosave.data.length
  ) {
    var data = core.saves.autosave.data.splice(core.saves.autosave.now, 1)[0];
    core.control.autosave(false);
    callback(id, data);
  }
  return;
};

control.prototype._doSL_load_afterGet = function (id, data) {
  if (!data) return alert("无效的存档");
  var _replay = function () {
    core.startGame(
      data.hard,
      data.hero.flags.__seed__,
      core.decodeRoute(data.route)
    );
  };
  if (data.version != core.firstData.version) {
    core.myconfirm(
      "存档版本不匹配!\n你想回放此存档的录像吗?\n可以随时停止录像播放以继续游戏。",
      _replay
    );
    return;
  }
  if (data.hero.flags.__events__ && data.guid != core.getGuid()) {
    core.myconfirm("此存档可能存在风险,你想要播放录像么?", _replay);
    return;
  }
  core.ui.closePanel();
  core.loadData(data, function () {
    core.removeFlag("__fromLoad__");
    core.drawTip("读档成功");
    if (id != "autoSave") {
      core.saves.saveIndex = id;
      core.setLocalStorage("saveIndex", core.saves.saveIndex);
    }
  });
};

control.prototype._doSL_replayLoad_afterGet = function (id, data) {
  if (!data) {
    core.playSound("操作失败");
    return core.drawTip("无效的存档");
  }
  if (data.version != core.firstData.version) {
    core.playSound("操作失败");
    return core.drawTip("存档版本不匹配");
  }
  if (data.hero.flags.__events__ && data.guid != core.getGuid()) {
    core.playSound("操作失败");
    return core.drawTip("此存档可能存在风险,无法读档");
  }
  var route = core.subarray(core.status.route, core.decodeRoute(data.route));
  if (route == null) {
    core.playSound("操作失败");
    return core.drawTip("无法从此存档回放录像");
  }
  core.loadData(data, function () {
    core.removeFlag("__fromLoad__");
    core.startReplay(route);
    core.drawTip("回退到存档节点");
  });
};

control.prototype._doSL_replayRemain_afterGet = function (id, data) {
  if (!data) {
    core.playSound("操作失败");
    return core.drawTip("无效的存档");
  }
  var route = core.decodeRoute(data.route);
  if (core.status.tempRoute) {
    var remainRoute = core.subarray(route, core.status.tempRoute);
    if (remainRoute == null)
      return alert(
        "无法接续播放录像!\n该存档必须是前一个选择的存档的后续内容。"
      );
    delete core.status.tempRoute;
    core.ui.closePanel();
    core.startReplay(remainRoute);
    core.drawTip("接续播放录像");
    return;
  } else if (
    data.floorId != core.status.floorId ||
    data.hero.loc.x != core.getHeroLoc("x") ||
    data.hero.loc.y != core.getHeroLoc("y")
  )
    return alert("楼层或坐标不一致!");

  core.status.tempRoute = route;
  core.ui.closePanel();
  core.drawText(
    "\t[步骤2]请选择第二个存档。\n\r[yellow]该存档必须是前一个存档的后续。\r\n将尝试播放到此存档。",
    function () {
      core.status.event.id = "replayRemain";
      core.lockControl();
      var saveIndex = core.saves.saveIndex;
      var page = parseInt((saveIndex - 1) / 5),
        offset = saveIndex - 5 * page;
      core.ui._drawSLPanel(10 * page + offset);
    }
  );
};

control.prototype._doSL_replaySince_afterGet = function (id, data) {
  if (
    data.floorId != core.status.floorId ||
    data.hero.loc.x != core.getHeroLoc("x") ||
    data.hero.loc.y != core.getHeroLoc("y")
  )
    return alert("楼层或坐标不一致!");
  if (!data.__toReplay__) return alert("该存档没有剩余录像!");
  core.ui.closePanel();
  core.startReplay(core.decodeRoute(data.__toReplay__));
  core.drawTip("播放存档剩余录像");
  return;
};

////// 同步存档到服务器 //////
control.prototype.syncSave = function (type) {
  core.ui.drawWaiting("正在同步,请稍候...");
  var callback = function (saves) {
    core.control._syncSave_http(type, saves);
  };
  if (type == "all") core.getAllSaves(callback);
  else core.getSave(core.saves.saveIndex, callback);
};

control.prototype._syncSave_http = function (type, saves) {
  if (!saves) return core.drawText("没有要同步的存档");
  var formData = new FormData();
  formData.append("type", "save");
  formData.append("name", core.firstData.name);
  formData.append("data", LZString.compressToBase64(JSON.stringify(saves)));
  formData.append("shorten", "1");

  core.http(
    "POST",
    "/games/sync.php",
    formData,
    function (data) {
      var response = JSON.parse(data);
      if (response.code < 0) {
        core.drawText(
          "出错啦!\n无法同步存档到服务器。\n错误原因:" + response.msg
        );
      } else {
        core.drawText(
          (type == "all" ? "所有存档" : "存档" + core.saves.saveIndex) +
            "同步成功!\n\n您的存档编号+密码: \r[yellow]" +
            response.code +
            response.msg +
            "\r\n\n请牢记以上信息(如截图等),在从服务器\n同步存档时使用。\n\r[yellow]另外请注意,存档同步只会保存一个月的时间。\r"
        );
      }
    },
    function (e) {
      core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:" + e);
    }
  );
};

////// 从服务器加载存档 //////
control.prototype.syncLoad = function () {
  core.myprompt("请输入存档编号+密码", null, function (idpassword) {
    if (!idpassword) return core.ui._drawSyncSave();
    if (!/^\d{6}\w{4}$/.test(idpassword) && !/^\d{4}\w{3}$/.test(idpassword)) {
      core.drawText("不合法的存档编号+密码!");
      return;
    }
    core.ui.drawWaiting("正在同步,请稍候...");
    if (idpassword.length == 7) {
      core.control._syncLoad_http(
        idpassword.substring(0, 4),
        idpassword.substring(4)
      );
    } else {
      core.control._syncLoad_http(
        idpassword.substring(0, 6),
        idpassword.substring(6)
      );
    }
  });
};

control.prototype._syncLoad_http = function (id, password) {
  var formData = new FormData();
  formData.append("type", "load");
  formData.append("name", core.firstData.name);
  formData.append("id", id);
  formData.append("password", password);

  core.http(
    "POST",
    "/games/sync.php",
    formData,
    function (data) {
      var response = JSON.parse(data);
      if (response.code == 0) {
        var msg = null;
        try {
          msg = JSON.parse(LZString.decompressFromBase64(response.msg));
        } catch (e) {}
        if (!msg) {
          try {
            msg = JSON.parse(response.msg);
          } catch (e) {}
        }
        if (msg) {
          core.control._syncLoad_write(msg);
        } else {
          core.drawText("出错啦!\n存档解析失败!");
        }
      } else {
        core.drawText(
          "出错啦!\n无法从服务器同步存档。\n错误原因:" + response.msg
        );
      }
    },
    function (e) {
      core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:" + e);
    }
  );
};

control.prototype._syncLoad_write = function (data) {
  if (data instanceof Array) {
    core.status.event.selection = 1;
    core.ui.drawConfirmBox(
      "所有本地存档都将被覆盖,确认?",
      function () {
        for (var i = 1; i <= 5 * (main.savePages || 30); i++) {
          if (i <= data.length) core.setLocalForage("save" + i, data[i - 1]);
          else if (core.saves.ids[i]) core.removeLocalForage("save" + i);
        }
        core.ui.closePanel();
        core.drawText("同步成功!\n你的本地所有存档均已被覆盖。");
      },
      function () {
        core.status.event.selection = 0;
        core.ui._drawSyncSave();
      }
    );
  } else {
    // 只覆盖单存档
    core.setLocalForage("save" + core.saves.saveIndex, data, function () {
      core.drawText("同步成功!\n单存档已覆盖至存档" + core.saves.saveIndex);
    });
  }
};

////// 存档到本地 //////
control.prototype.saveData = function () {
  return this.controldata.saveData();
};

////// 从本地读档 //////
control.prototype.loadData = function (data, callback) {
  return this.controldata.loadData(data, callback);
};

control.prototype.getSave = function (index, callback) {
  if (index == 0) {
    // --- 自动存档先从缓存中获取
    if (core.saves.autosave.data != null) callback(core.saves.autosave.data);
    else {
      core.getLocalForage(
        "autoSave",
        null,
        function (data) {
          if (data != null) {
            core.saves.autosave.data = data;
            if (!(core.saves.autosave.data instanceof Array)) {
              core.saves.autosave.data = [core.saves.autosave.data];
            }
            core.saves.autosave.now = core.saves.autosave.data.length;
          }
          callback(core.saves.autosave.data);
        },
        function (err) {
          console.error(err);
          callback(null);
        }
      );
    }
    return;
  }
  core.getLocalForage(
    "save" + index,
    null,
    function (data) {
      if (callback) callback(data);
    },
    function (err) {
      console.error(err);
      if (callback) callback(null);
    }
  );
};

control.prototype.getSaves = function (ids, callback) {
  if (!(ids instanceof Array)) return this.getSave(ids, callback);
  var count = ids.length,
    data = {};
  for (var i = 0; i < ids.length; ++i) {
    (function (i) {
      core.getSave(ids[i], function (result) {
        data[i] = result;
        if (Object.keys(data).length == count) callback(data);
      });
    })(i);
  }
};

control.prototype.getAllSaves = function (callback) {
  var ids = Object.keys(core.saves.ids)
      .filter(function (x) {
        return x != 0;
      })
      .sort(function (a, b) {
        return a - b;
      }),
    saves = [];
  this.getSaves(ids, function (data) {
    for (var i = 0; i < ids.length; ++i) {
      if (data[i] != null) saves.push(data[i]);
    }
    callback(saves);
  });
};

////// 获得所有存在存档的存档位 //////
control.prototype.getSaveIndexes = function (callback) {
  var indexes = {};
  core.keysLocalForage(function (err, keys) {
    if (err) {
      console.error(err);
      return callback(indexes);
    }
    keys.forEach(function (key) {
      core.control._getSaveIndexes_getIndex(indexes, key);
    });
    callback(indexes);
  });
};

control.prototype._getSaveIndexes_getIndex = function (indexes, name) {
  var e = new RegExp("^" + core.firstData.name + "_(save\\d+|autoSave)$").exec(
    name
  );
  if (e) {
    if (e[1] == "autoSave") indexes[0] = true;
    else indexes[parseInt(e[1].substring(4))] = true;
  }
};

////// 判断某个存档位是否存在存档 //////
control.prototype.hasSave = function (index) {
  return core.saves.ids[index] || false;
};

////// 删除某个存档
control.prototype.removeSave = function (index, callback) {
  if (index == 0 || index == "autoSave") {
    index = "autoSave";
    core.removeLocalForage(index, function () {
      core.saves.autosave.data = null;
      core.saves.autosave.updated = false;
      if (callback) callback();
    });
    return;
  }
  core.removeLocalForage(
    "save" + index,
    function () {
      core.saves.favorite = core.saves.favorite.filter(function (i) {
        return core.hasSave(i);
      });
      delete core.saves.favoriteName[index];
      core.control._updateFavoriteSaves();
      if (callback) callback();
    },
    function () {
      core.playSound("操作失败");
      core.drawTip("无法删除存档!");
      if (callback) callback();
    }
  );
};

////// 读取收藏信息
control.prototype._loadFavoriteSaves = function () {
  core.saves.favorite = core.getLocalStorage("favorite", []);
  // --- 移除不存在的收藏
  core.saves.favorite = core.saves.favorite.filter(function (i) {
    return core.hasSave(i);
  });
  core.saves.favoriteName = core.getLocalStorage("favoriteName", {});
};

control.prototype._updateFavoriteSaves = function () {
  core.setLocalStorage("favorite", core.saves.favorite);
  core.setLocalStorage("favoriteName", core.saves.favoriteName);
};

// ------ 属性,状态,位置,buff,变量,锁定控制等 ------ //

////// 设置勇士属性 //////
control.prototype.setStatus = function (name, value) {
  if (!core.status.hero) return;
  if (name == "x" || name == "y" || name == "direction")
    this.setHeroLoc(name, value);
  else core.status.hero[name] = value;
};

////// 增减勇士属性 //////
control.prototype.addStatus = function (name, value) {
  this.setStatus(name, this.getStatus(name) + value);
};

////// 获得勇士属性 //////
control.prototype.getStatus = function (name) {
  if (!core.status.hero) return null;
  if (name == "x" || name == "y" || name == "direction")
    return this.getHeroLoc(name);
  if (main.mode == "editor" && !core.hasFlag("__statistics__")) {
    return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero[name];
  }
  return core.status.hero[name];
};

////// 从status中获得属性,如果不存在则从勇士属性中获取 //////
control.prototype.getStatusOrDefault = function (status, name) {
  if (status && name in status) return Math.floor(status[name]);
  return Math.floor(this.getStatus(name));
};

////// 获得勇士实际属性(增幅后的) //////
control.prototype.getRealStatus = function (name) {
  return this.getRealStatusOrDefault(null, name);
};

////// 从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取 //////
control.prototype.getRealStatusOrDefault = function (status, name) {
  return Math.floor(this.getStatusOrDefault(status, name) * this.getBuff(name));
};

////// 获得勇士原始属性(无装备和衰弱影响) //////
control.prototype.getNakedStatus = function (name) {
  var value = this.getStatus(name);
  if (value == null) return value;
  // 装备增幅
  core.status.hero.equipment.forEach(function (v) {
    if (!v || !(core.material.items[v] || {}).equip) return;
    value -= core.material.items[v].equip.value[name] || 0;
  });
  // 衰弱扣除
  if (
    core.hasFlag("weak") &&
    core.values.weakValue >= 1 &&
    (name == "atk" || name == "def")
  ) {
    value += core.values.weakValue;
  }
  return value;
};

////// 获得某个属性的名字 //////
control.prototype.getStatusLabel = function (name) {
  if (this.controldata.getStatusLabel) {
    return this.controldata.getStatusLabel(name) || name;
  }
  return (
    {
      name: "名称",
      lv: "等级",
      hpmax: "生命上限",
      hp: "生命",
      manamax: "魔力上限",
      mana: "魔力",
      atk: "攻击",
      def: "防御",
      mdef: "护盾",
      money: "金币",
      exp: "经验",
      point: "加点",
      steps: "步数",
    }[name] || name
  );
};

////// 设置某个属性的增幅值 //////
control.prototype.setBuff = function (name, value) {
  // 仅保留三位有效buff值
  value = parseFloat(value.toFixed(3));
  this.setFlag("__" + name + "_buff__", value);
};

////// 加减某个属性的增幅值 //////
control.prototype.addBuff = function (name, value) {
  var buff = this.getBuff(name) + value;
  // 仅保留三位有效buff值
  buff = parseFloat(buff.toFixed(3));
  this.setFlag("__" + name + "_buff__", buff);
};

////// 获得某个属性的增幅值 //////
control.prototype.getBuff = function (name) {
  return core.getFlag("__" + name + "_buff__", 1);
};

////// 获得或移除毒衰咒效果 //////
control.prototype.triggerDebuff = function (action, type) {
  return this.controldata.triggerDebuff(action, type);
};

////// 设置勇士的位置 //////
control.prototype.setHeroLoc = function (name, value, noGather) {
  if (!core.status.hero) return;
  core.status.hero.loc[name] = value;
  if ((name == "x" || name == "y") && !noGather) {
    this.gatherFollowers();
  }
  core.ui.drawStatusBar();
};

////// 获得勇士的位置 //////
control.prototype.getHeroLoc = function (name) {
  if (!core.status.hero) return;
  if (main.mode == "editor") {
    if (name == null)
      return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero.loc;
    return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero.loc[name];
  }
  if (name == null) return core.status.hero.loc;
  return core.status.hero.loc[name];
};

////// 获得某个等级的名称 //////
control.prototype.getLvName = function (lv) {
  if (!core.status.hero) return null;
  if (lv == null) lv = core.status.hero.lv;
  return ((core.firstData.levelUp || [])[lv - 1] || {}).title || lv;
};

////// 获得下个等级所需经验;如果不存在下个等级,返回null。 //////
control.prototype.getNextLvUpNeed = function () {
  if (!core.status.hero) return null;
  if (core.status.hero.lv >= core.firstData.levelUp.length) return null;
  var need = core.calValue(core.firstData.levelUp[core.status.hero.lv].need);
  if (core.flags.statusBarItems.indexOf("levelUpLeftMode") >= 0)
    return Math.max(need - core.getStatus("exp"), 0);
  else return need;
};

////// 设置某个自定义变量或flag //////
control.prototype.setFlag = function (name, value) {
  if (value == null) return this.removeFlag(name);
  if (!core.status.hero) return;
  core.status.hero.flags[name] = value;
};

////// 增加某个flag数值 //////
control.prototype.addFlag = function (name, value) {
  if (!core.status.hero) return;
  core.setFlag(name, core.getFlag(name, 0) + value);
};

////// 获得某个自定义变量或flag //////
control.prototype.getFlag = function (name, defaultValue) {
  if (!core.status.hero) return defaultValue;
  var value = core.status.hero.flags[name];
  return value != null ? value : defaultValue;
};

////// 是否存在某个自定义变量或flag,且值为true //////
control.prototype.hasFlag = function (name) {
  return !!core.getFlag(name);
};

////// 删除某个自定义变量或flag //////
control.prototype.removeFlag = function (name) {
  if (!core.status.hero) return;
  delete core.status.hero.flags[name];
};

////// 获得某个点的独立开关 //////
control.prototype.getSwitch = function (x, y, floorId, name, defaultValue) {
  var prefix = [
    floorId || core.status.floorId || ":f",
    x != null ? x : "x",
    y != null ? y : "y",
  ].join("@");
  return this.getFlag(prefix + "@" + name, defaultValue);
};

////// 设置某个点的独立开关 //////
control.prototype.setSwitch = function (x, y, floorId, name, value) {
  var prefix = [
    floorId || core.status.floorId || ":f",
    x != null ? x : "x",
    y != null ? y : "y",
  ].join("@");
  return this.setFlag(prefix + "@" + name, value);
};

////// 增加某个点的独立开关 //////
control.prototype.addSwitch = function (x, y, floorId, name, value) {
  var prefix = [
    floorId || core.status.floorId || ":f",
    x != null ? x : "x",
    y != null ? y : "y",
  ].join("@");
  return this.addFlag(prefix + "@" + name, value);
};

////// 判定某个点的独立开关 //////
control.prototype.hasSwitch = function (x, y, floorId, name) {
  var prefix = [
    floorId || core.status.floorId || ":f",
    x != null ? x : "x",
    y != null ? y : "y",
  ].join("@");
  return this.hasFlag(prefix + "@" + name);
};

////// 删除某个点的独立开关 //////
control.prototype.removeSwitch = function (x, y, floorId, name) {
  var prefix = [
    floorId || core.status.floorId || ":f",
    x != null ? x : "x",
    y != null ? y : "y",
  ].join("@");
  return this.removeFlag(prefix + "@" + name);
};

////// 锁定状态栏,常常用于事件处理 //////
control.prototype.lockControl = function () {
  core.status.lockControl = true;
};

////// 解锁状态栏 //////
control.prototype.unlockControl = function () {
  core.status.lockControl = false;
};

////// 开启debug模式 //////
control.prototype.debug = function () {
  core.setFlag("debug", true);
  core.drawText(
    "\t[调试模式开启]此模式下按住Ctrl键(或Ctrl+Shift键)可以穿墙并忽略一切事件。\n此模式下将无法上传成绩。"
  );
};

control.prototype._bindRoutePush = function () {
  core.status.route.push = function (element) {
    // 忽视移动、转向、瞬移
    if (
      ["up", "down", "left", "right", "turn"].indexOf(element) < 0 &&
      !element.startsWith("move:")
    ) {
      core.clearRouteFolding();
    }
    Array.prototype.push.call(core.status.route, element);
  };
};

////// 清除录像折叠信息 //////
control.prototype.clearRouteFolding = function () {
  core.status.routeFolding = {};
};

////// 检查录像折叠 //////
control.prototype.checkRouteFolding = function () {
  // 未开启、未开始游戏、录像播放中、正在事件中:不执行
  if (
    !core.flags.enableRouteFolding ||
    !core.isPlaying() ||
    core.isReplaying() ||
    core.status.event.id
  ) {
    return this.clearRouteFolding();
  }
  var hero = core.clone(core.status.hero, function (name, value) {
    return name != "steps" && typeof value == "number";
  });
  var index = [
    core.getHeroLoc("x"),
    core.getHeroLoc("y"),
    core.getHeroLoc("direction").charAt(0),
  ].join(",");
  core.status.routeFolding = core.status.routeFolding || {};
  if (core.status.routeFolding[index]) {
    var one = core.status.routeFolding[index];
    if (core.same(one.hero, hero) && one.length < core.status.route.length) {
      Object.keys(core.status.routeFolding).forEach(function (v) {
        if (core.status.routeFolding[v].length >= one.length)
          delete core.status.routeFolding[v];
      });
      core.status.route = core.status.route.slice(0, one.length);
      this._bindRoutePush();
    }
  }
  core.status.routeFolding[index] = {
    hero: hero,
    length: core.status.route.length,
  };
};

// ------ 天气,色调,BGM ------ //

control.prototype.getMappedName = function (name) {
  return (
    core.getFlag("__nameMap__", {})[name] || (main.nameMap || {})[name] || name
  );
};

////// 更改天气效果 //////
control.prototype.setWeather = function (type, level) {
  // 非雨雪
  if (type == null || !this.weathers[type]) {
    core.deleteCanvas("weather");
    core.animateFrame.weather.type = null;
    core.animateFrame.weather.nodes = [];
    return;
  }
  if (level == null) level = core.animateFrame.weather.level;
  level = core.clamp(parseInt(level) || 5, 1, 10);
  // 当前天气:则忽略
  if (
    type == core.animateFrame.weather.type &&
    level == core.animateFrame.weather.level
  )
    return;

  // 计算当前的宽高
  core.createCanvas("weather", 0, 0, core._PX_, core._PY_, 80);
  core.setOpacity("weather", 1.0);
  core.animateFrame.weather.type = type;
  core.animateFrame.weather.level = level;
  core.animateFrame.weather.nodes = [];
  try {
    core.doFunc(this.weathers[type].initFunc, this, level);
  } catch (e) {
    console.error(e);
    console.error("ERROR in weather[" + type + "]:已自动注销该项。");
    core.unregisterWeather(type);
  }
};

////// 注册一个天气 //////
// name为天气类型,如 sun, rain, snow 等
// initFunc 为设置为此天气时的初始化,接受level参数
// frameFunc 为该天气下每帧的效果,接受和timestamp参数(从页面加载完毕到当前经过的时间)
control.prototype.registerWeather = function (name, initFunc, frameFunc) {
  this.unregisterWeather(name);
  this.weathers[name] = { initFunc: initFunc, frameFunc: frameFunc };
};

////// 取消注册一个天气 //////
control.prototype.unregisterWeather = function (name) {
  delete this.weathers[name];
  if (core.animateFrame.weather.type == name) {
    this.setWeather(null);
  }
};

control.prototype._weather_rain = function (level) {
  var number =
    level *
    parseInt(
      (20 * core.bigmap.width * core.bigmap.height) /
        (core._WIDTH_ * core._HEIGHT_)
    );
  for (var a = 0; a < number; a++) {
    core.animateFrame.weather.nodes.push({
      x: Math.random() * core.bigmap.width * 32,
      y: Math.random() * core.bigmap.height * 32,
      l: Math.random() * 2.5,
      xs: -4 + Math.random() * 4 + 2,
      ys: Math.random() * 10 + 10,
    });
  }
};

control.prototype._weather_snow = function (level) {
  var number =
    level *
    parseInt(
      (20 * core.bigmap.width * core.bigmap.height) /
        (core._WIDTH_ * core._HEIGHT_)
    );
  for (var a = 0; a < number; a++) {
    core.animateFrame.weather.nodes.push({
      x: Math.random() * core.bigmap.width * 32,
      y: Math.random() * core.bigmap.height * 32,
      r: Math.random() * 5 + 1,
      d: Math.random() * Math.min(level, 200),
    });
  }
};

control.prototype._weather_fog = function (level) {
  if (!core.animateFrame.weather.fog) return;
  core.animateFrame.weather.nodes = [
    {
      image: core.animateFrame.weather.fog,
      level: 40 * level,
      x: 0,
      y: -core._PY_ / 2,
      dx: -Math.random() * 1.5,
      dy: Math.random(),
      delta: 0.001,
    },
  ];
};

control.prototype._weather_cloud = function (level) {
  if (!core.animateFrame.weather.cloud) return;
  core.animateFrame.weather.nodes = [
    {
      image: core.animateFrame.weather.cloud,
      level: 40 * level,
      x: 0,
      y: -core._PY_ / 2,
      dx: -Math.random() * 1.5,
      dy: Math.random(),
      delta: 0.001,
    },
  ];
};

control.prototype._weather_sun = function (level) {
  if (!core.animateFrame.weather.sun) return;
  // 直接绘制
  core.clearMap("weather");
  core.drawImage(
    "weather",
    core.animateFrame.weather.sun,
    0,
    0,
    core.animateFrame.weather.sun.width,
    core.animateFrame.weather.sun.height,
    0,
    0,
    core._PX_,
    core._PY_
  );
  core.setOpacity("weather", level / 10);
  core.animateFrame.weather.nodes = [{ opacity: level / 10, delta: 0.01 }];
};

////// 更改画面色调 //////
control.prototype.setCurtain = function (color, time, moveMode, callback) {
  if (time == null) time = 750;
  if (time <= 0) time = 0;
  if (!core.status.curtainColor) core.status.curtainColor = [0, 0, 0, 0];
  if (!color) color = [0, 0, 0, 0];
  if (color[3] == null) color[3] = 1;
  color[3] = core.clamp(color[3], 0, 1);

  if (time == 0) {
    // 直接变色
    core.clearMap("curtain");
    core.fillRect(
      "curtain",
      0,
      0,
      core._PX_,
      core._PY_,
      core.arrayToRGBA(color)
    );
    core.status.curtainColor = color;
    if (callback) callback();
    return;
  }

  this._setCurtain_animate(
    core.status.curtainColor,
    color,
    time,
    moveMode,
    callback
  );
};

control.prototype._setCurtain_animate = function (
  nowColor,
  color,
  time,
  moveMode,
  callback
) {
  time /= Math.max(core.status.replay.speed, 1);
  var per_time = 10,
    step = 0,
    steps = parseInt(time / per_time);
  if (steps <= 0) steps = 1;
  var curr = nowColor;
  var moveFunc = core.applyEasing(moveMode);

  var cb = function () {
    core.status.curtainColor = curr;
    if (callback) callback();
  };
  var animate = setInterval(function () {
    step++;
    curr = [
      nowColor[0] + (color[0] - nowColor[0]) * moveFunc(step / steps),
      nowColor[1] + (color[1] - nowColor[1]) * moveFunc(step / steps),
      nowColor[2] + (color[2] - nowColor[2]) * moveFunc(step / steps),
      nowColor[3] + (color[3] - nowColor[3]) * moveFunc(step / steps),
    ];
    core.clearMap("curtain");
    core.fillRect(
      "curtain",
      0,
      0,
      core._PX_,
      core._PY_,
      core.arrayToRGBA(curr)
    );
    if (step == steps) {
      delete core.animateFrame.asyncId[animate];
      clearInterval(animate);
      cb();
    }
  }, per_time);

  core.animateFrame.lastAsyncId = animate;
  core.animateFrame.asyncId[animate] = cb;
};

////// 画面闪烁 //////
control.prototype.screenFlash = function (
  color,
  time,
  times,
  moveMode,
  callback
) {
  times = times || 1;
  time = time / 3;
  var nowColor = core.clone(core.status.curtainColor);
  core.setCurtain(color, time, moveMode, function () {
    core.setCurtain(nowColor, time * 2, moveMode, function () {
      if (times > 1)
        core.screenFlash(color, time * 3, times - 1, moveMode, callback);
      else {
        if (callback) callback();
      }
    });
  });
};

////// 播放背景音乐 //////
control.prototype.playBgm = function (bgm, startTime) {
  bgm = core.getMappedName(bgm);
  if (main.mode != "play" || !core.material.bgms[bgm]) return;
  // 如果不允许播放
  if (!core.musicStatus.bgmStatus) {
    try {
      core.musicStatus.playingBgm = bgm;
      core.musicStatus.lastBgm = bgm;
      core.material.bgms[bgm].pause();
    } catch (e) {
      console.error(e);
    }
    return;
  }
  this.setMusicBtn();

  try {
    this._playBgm_play(bgm, startTime);
  } catch (e) {
    console.log("无法播放BGM " + bgm);
    console.error(e);
    core.musicStatus.playingBgm = null;
  }
};

control.prototype._playBgm_play = function (bgm, startTime) {
  // 如果当前正在播放,且和本BGM相同,直接忽略
  if (
    core.musicStatus.playingBgm == bgm &&
    !core.material.bgms[core.musicStatus.playingBgm].paused
  ) {
    return;
  }
  // 如果正在播放中,暂停
  if (core.musicStatus.playingBgm) {
    core.material.bgms[core.musicStatus.playingBgm].pause();
  }
  // 缓存BGM
  core.loader.loadBgm(bgm);
  // 播放当前BGM
  core.material.bgms[bgm].volume =
    core.musicStatus.userVolume * core.musicStatus.designVolume;
  core.material.bgms[bgm].currentTime = startTime || 0;
  core.material.bgms[bgm].play();
  core.musicStatus.playingBgm = bgm;
  core.musicStatus.lastBgm = bgm;
  core.setBgmSpeed(100);
};

///// 设置当前背景音乐的播放速度 //////
control.prototype.setBgmSpeed = function (speed, usePitch) {
  var bgm = core.musicStatus.playingBgm;
  if (main.mode != "play" || !core.material.bgms[bgm]) return;
  bgm = core.material.bgms[bgm];
  if (speed < 30 || speed > 300) return;
  bgm.playbackRate = speed / 100;
  core.musicStatus.bgmSpeed = speed;

  if (bgm.preservesPitch != null) {
    if (bgm.__preservesPitch == null) bgm.__preservesPitch = bgm.preservesPitch;
    if (usePitch == null) bgm.preservesPitch = bgm.__preservesPitch;
    else if (usePitch) bgm.preservesPitch = false;
    else bgm.preservesPitch = true;
    core.musicStatus.bgmUsePitch = usePitch;
  }
};

////// 暂停背景音乐的播放 //////
control.prototype.pauseBgm = function () {
  if (main.mode != "play") return;
  try {
    if (core.musicStatus.playingBgm) {
      core.musicStatus.pauseTime =
        core.material.bgms[core.musicStatus.playingBgm].currentTime;
      core.material.bgms[core.musicStatus.playingBgm].pause();
      core.musicStatus.playingBgm = null;
    }
  } catch (e) {
    console.log("无法暂停BGM");
    console.error(e);
  }
  this.setMusicBtn();
};

////// 恢复背景音乐的播放 //////
control.prototype.resumeBgm = function (resumeTime) {
  if (main.mode != "play") return;
  try {
    var speed = core.musicStatus.bgmSpeed;
    var usePitch = core.musicStatus.bgmUsePitch;
    core.playBgm(
      core.musicStatus.playingBgm || core.musicStatus.lastBgm || main.startBgm,
      resumeTime ? core.musicStatus.pauseTime : 0
    );
    if (resumeTime) {
      core.setBgmSpeed(speed, usePitch);
    }
  } catch (e) {
    console.log("无法恢复BGM");
    console.error(e);
  }
  this.setMusicBtn();
};

control.prototype.setMusicBtn = function () {
  if (core.musicStatus.bgmStatus)
    core.dom.musicBtn.src =
      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABWVBMVEX///9iYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmL///8AAAC5ubn+/v6xsbEtLS0MDAxmZmZoaGhvb2/c3Nzd3d38/Pz9/f0oKCgpKSl0dHR1dXW6urrb29v7+/v09PTv7+/39/cgICACAgImJibh4eGFhYWGhoaHh4eOjo5paWm7u7vDw8PMzMwyMjI7OztAQEDe3t5FRUVMTEzj4+Pl5eXm5ubp6enr6+tcXFzi4uL19fVeXl74+PgjIyNkZGQGBgaSkpKYmJiampqenp4DAwMwMDBnZ2cICAivr68eHh63t7cLCwsSEhLw8PBhYWEUFBQVFRXNzc3Pz8/Z2dna2toaGhqkpKSlpaWpqamrq6tFOUNAAAAAc3RSTlMAAwQFBhUWGxwkJSYyO0dISVBRUmpvj5CSk5SVoaOlpqiysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyA0IuUgAAAVdJREFUeF5NkVVbw0AQRTcQrLR4IIEGcidJoaUuQHF3d3d3+P/CkuxCzss8nG++mbnDBJXhNt2CpbeFK1kQpSEKidlc8S9qdATRa6UIdQMoxEpDA0Ov3wUAPfW+qLWACydNv9zMrzkJwPK6FB3oHyOfXfuNxvoBQ+GmBYinhHB77TmiVBxoYUw1AYcEq332AS8OYKosAuTT0nza9uU2USYPRJgGxEiSOFywJ3mNARozgBJJzkfLvfu8JgGDWcC9FEsjWzR+y80gYDEAA8QZ3N6kmP1Fs3fEASB7pob7Hh+Wz5L0ci17Or05J7bH6B6dZv05XWK3rG+myV05Ert592Qo55sPuoIr7hEZHHtieIPWy0RU9DLwc3Mnck/vi8/E8XNrDWQtEVnL/ySKMrv0jPwPp870fprcyYifmiEmqGpHkI5q9ofSFIUk2qiwIGpEMyxYhhZRRcMPz89RJ2s9W8wAAAAASUVORK5CYII=";
  else
    core.dom.musicBtn.src =
      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABYlBMVEX///9iYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmL////8/PwAAABmZmZoaGihoaGioqKxsbG5ubnb29vc3Nzd3d3h4eHi4uL9/f3+/v4tLS1nZ2d0dHSUlJSenp66uroMDAz7+/spKSkoKCgUFBRpaWkVFRVvb291dXU7OzuVlZWYmJhkZGQgICAjIyOkpKQCAgK3t7cGBgbv7++pqamrq6seHh4mJiZhYWGamprp6enr6+saGhpeXl7j4+Pl5eXm5uZKSkrw8PD09PT19fW7u7vDw8PMzMwICAgwMDAyMjILCwtAQECGhoaHh4eBgYGFhYUSEhJXV1dZWVlcXFyOjo6SkpLNzc339/fPz8/Z2dna2tqTk5OlpaWxOPeTAAAAdnRSTlMAAwQFBhUWGxwkJSYyO0dISVBRUmpvj5CSk5SVoaOlpqiysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyNuo+uwAAAWJJREFUeF5NkmV34zAQReUm7WbTuJBNunY3bvXGDjNTkZkZlpn5/9eR5FPfbzr3jGb0RkwRiMQMDm7EIgHmRxtLwMOaHHoQjwz4MUKeCM8AWMrmd7u7f/aXAMyOShHiQD1n04DtN5e5FMBFlSauIsm585dKi4CpuSYKJIv1tBDVmvOSqJgEoowFLSBHaQh10XHWiCgHWEGmAw2blPrvOK/KRJUGoLM4kCVSKrWz7HwgoiwQZyaQJ0+9PvxV23BNATAZB25IqX9b3+jTW9fcApwB6NLgUD5NY3mPXnwmFwBezff1ztzRFzTp94FXMy36HDuCa2RafdnnmZqtL818Gl9/qNnEeyrUk2aTPiKj3qMyWBVi/YSuWq5qiwxkbtX3vYWzdz/l8M0k8ERlvViiB1Ygslb7SbVtJezncj+Cx5bYaeGuonZqhZlieAp+no74/s5EAh6JcY35Cepxk4ObcT3IJPe/1lKsDpFCFQAAAABJRU5ErkJggg==";
};

////// 更改背景音乐的播放 //////
control.prototype.triggerBgm = function () {
  if (main.mode != "play") return;

  core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus;
  if (core.musicStatus.bgmStatus) this.resumeBgm();
  else this.pauseBgm();
  core.setLocalStorage("bgmStatus", core.musicStatus.bgmStatus);
};

////// 播放音频 //////
control.prototype.playSound = function (sound, pitch, callback) {
  sound = core.getMappedName(sound);
  if (
    main.mode != "play" ||
    !core.musicStatus.soundStatus ||
    !core.material.sounds[sound]
  )
    return;
  try {
    if (core.musicStatus.audioContext != null) {
      var source = core.musicStatus.audioContext.createBufferSource();
      source.__name = sound;
      source.buffer = core.material.sounds[sound];
      source.connect(core.musicStatus.gainNode);
      var id = setTimeout(null);
      if (pitch && pitch >= 30 && pitch <= 300) {
        source.playbackRate.setValueAtTime(pitch / 100, 0);
      }
      source.onended = function () {
        delete core.musicStatus.playingSounds[id];
        if (callback) callback();
      };
      core.musicStatus.playingSounds[id] = source;
      if (source.start) source.start(0);
      else if (source.noteOn) source.noteOn(0);
      return id;
    } else {
      core.material.sounds[sound].volume = core.musicStatus.userVolume;
      core.material.sounds[sound].play();
      if (callback) callback();
    }
  } catch (e) {
    console.log("无法播放SE " + sound);
    console.error(e);
  }
};

////// 停止所有音频 //////
control.prototype.stopSound = function (id) {
  if (id == null) {
    Object.keys(core.musicStatus.playingSounds).forEach(function (id) {
      core.control.stopSound(id);
    });
    return;
  }
  var source = core.musicStatus.playingSounds[id];
  if (!source) return;
  try {
    if (source.stop) source.stop();
    else if (source.noteOff) source.noteOff();
  } catch (e) {
    console.error(e);
  }
  delete core.musicStatus.playingSounds[id];
};

////// 获得当前正在播放的所有(指定)音效的id列表 //////
control.prototype.getPlayingSounds = function (name) {
  name = core.getMappedName(name);
  return Object.keys(core.musicStatus.playingSounds).filter(function (one) {
    return name == null || core.musicStatus.playingSounds[one].__name == name;
  });
};

////// 检查bgm状态 //////
control.prototype.checkBgm = function () {
  core.playBgm(core.musicStatus.playingBgm || main.startBgm);
};

///// 设置屏幕放缩 //////
control.prototype.setDisplayScale = function (delta) {
  var index = core.domStyle.availableScale.indexOf(core.domStyle.scale);
  if (index < 0) return;
  index =
    (index + delta + core.domStyle.availableScale.length) %
    core.domStyle.availableScale.length;
  core.domStyle.scale = core.domStyle.availableScale[index];
  core.setLocalStorage("scale", core.domStyle.scale);
  core.resize();
};

// ------ 状态栏,工具栏等相关 ------ //

////// 清空状态栏 //////
control.prototype.clearStatusBar = function () {
  Object.keys(core.statusBar).forEach(function (e) {
    if (core.statusBar[e].innerHTML != null) {
      core.statusBar[e].innerHTML = "&nbsp;";
      core.statusBar[e].removeAttribute("_style");
      core.statusBar[e].removeAttribute("_value");
    }
  });
  core.statusBar.image.book.style.opacity = 0.3;
  if (!core.flags.equipboxButton) core.statusBar.image.fly.style.opacity = 0.3;
};

////// 更新状态栏 //////
control.prototype.updateStatusBar = function (doNotCheckAutoEvents, immediate) {
  if (!core.isPlaying()) return;
  if (immediate) {
    return this.updateStatusBar_update();
  }
  if (!doNotCheckAutoEvents) this.noAutoEvents = false;
  if (core.isReplaying()) return this.updateStatusBar_update();
  if (!core.control.updateNextFrame) {
    core.control.updateNextFrame = true;
    requestAnimationFrame(this.updateStatusBar_update);
  }
};

control.prototype.updateStatusBar_update = function () {
  core.control.updateNextFrame = false;
  if (!core.isPlaying() || core.hasFlag("__statistics__")) return;
  core.control.controldata.updateStatusBar();
  if (!core.control.noAutoEvents) core.checkAutoEvents();
  core.control._updateStatusBar_setToolboxIcon();
  core.clearRouteFolding();
  core.control.noAutoEvents = true;
};

control.prototype._updateStatusBar_setToolboxIcon = function () {
  if (core.isReplaying()) {
    core.statusBar.image.book.src = core.status.replay.pausing
      ? core.statusBar.icons.play.src
      : core.statusBar.icons.pause.src;
    core.statusBar.image.book.style.opacity = 1;
    core.statusBar.image.fly.src = core.statusBar.icons.stop.src;
    core.statusBar.image.fly.style.opacity = 1;
    core.statusBar.image.toolbox.src = core.statusBar.icons.rewind.src;
    core.statusBar.image.keyboard.src = core.statusBar.icons.book.src;
    core.statusBar.image.shop.src = core.statusBar.icons.floor.src;
    core.statusBar.image.save.src = core.statusBar.icons.speedDown.src;
    core.statusBar.image.save.style.opacity = 1;
    core.statusBar.image.load.src = core.statusBar.icons.speedUp.src;
    core.statusBar.image.settings.src = core.statusBar.icons.save.src;
  } else {
    core.statusBar.image.book.src = core.statusBar.icons.book.src;
    core.statusBar.image.book.style.opacity = core.hasItem("book") ? 1 : 0.3;
    if (!core.flags.equipboxButton) {
      core.statusBar.image.fly.src = core.statusBar.icons.fly.src;
      core.statusBar.image.fly.style.opacity = core.hasItem("fly") ? 1 : 0.3;
    } else {
      core.statusBar.image.fly.src = core.statusBar.icons.equipbox.src;
      core.statusBar.image.fly.style.opacity = 1;
    }
    core.statusBar.image.toolbox.src = core.statusBar.icons.toolbox.src;
    core.statusBar.image.keyboard.src = core.statusBar.icons.keyboard.src;
    core.statusBar.image.shop.src = core.statusBar.icons.shop.src;
    core.statusBar.image.save.src = core.statusBar.icons.save.src;
    core.statusBar.image.save.style.opacity = core.hasFlag("__forbidSave__")
      ? 0.3
      : 1;
    core.statusBar.image.load.src = core.statusBar.icons.load.src;
    core.statusBar.image.settings.src = core.statusBar.icons.settings.src;
  }
};

control.prototype.showStatusBar = function () {
  if (main.mode == "editor") return;
  if (core.domStyle.showStatusBar) return;
  var statusItems = core.dom.status;
  core.domStyle.showStatusBar = true;
  core.removeFlag("hideStatusBar");
  // 显示
  for (var i = 0; i < statusItems.length; ++i) statusItems[i].style.opacity = 1;
  this.setToolbarButton(false);
  core.dom.tools.hard.style.display = "block";
  core.dom.toolBar.style.display = "block";
};

control.prototype.hideStatusBar = function (showToolbox) {
  if (main.mode == "editor") return;

  // 如果原本就是隐藏的,则先显示
  if (!core.domStyle.showStatusBar) this.showStatusBar();
  if (core.isReplaying()) showToolbox = true;

  var statusItems = core.dom.status,
    toolItems = core.dom.tools;
  core.domStyle.showStatusBar = false;
  core.setFlag("hideStatusBar", true);
  core.setFlag("showToolbox", showToolbox || null);
  // 隐藏
  for (var i = 0; i < statusItems.length; ++i) statusItems[i].style.opacity = 0;
  if (
    (!core.domStyle.isVertical && !core.flags.extendToolbar) ||
    !showToolbox
  ) {
    for (var i = 0; i < toolItems.length; ++i)
      toolItems[i].style.display = "none";
  }
  if (!core.domStyle.isVertical && !core.flags.extendToolbar) {
    core.dom.toolBar.style.display = "none";
  }
};

////// 更新状态栏的勇士图标 //////
control.prototype.updateHeroIcon = function (name) {
  name = name || "hero.png";
  if (core.statusBar.icons.name == name) return;
  core.statusBar.icons.name = name;

  var image = core.material.images.hero;
  // 全身图
  var w = core.material.icons.hero.width || 32;
  var h = core.material.icons.hero.height || 48;
  var ratio = Math.min(w / h, 1),
    width = 32 * ratio,
    left = 16 - width / 2;

  var canvas = document.createElement("canvas");
  var ctx = canvas.getContext("2d");
  canvas.width = 32;
  canvas.height = 32;
  core.drawImage(ctx, image, 0, 0, w, h, left, 0, width, 32);

  core.statusBar.image.name.src = canvas.toDataURL("image/png");
};

////// 改变工具栏为按钮1-8 //////
control.prototype.setToolbarButton = function (useButton) {
  if (!core.domStyle.showStatusBar) {
    // 隐藏状态栏时检查竖屏
    if (!core.domStyle.isVertical && !core.flags.extendToolbar) {
      for (var i = 0; i < core.dom.tools.length; ++i)
        core.dom.tools[i].style.display = "none";
      return;
    }
    if (!core.hasFlag("showToolbox")) return;
    else core.dom.tools.hard.style.display = "block";
  }

  if (useButton == null) useButton = core.domStyle.toolbarBtn;
  if (!core.domStyle.isVertical && !core.flags.extendToolbar) useButton = false;
  core.domStyle.toolbarBtn = useButton;

  if (useButton) {
    [
      "book",
      "fly",
      "toolbox",
      "keyboard",
      "shop",
      "save",
      "load",
      "settings",
    ].forEach(function (t) {
      core.statusBar.image[t].style.display = "none";
    });
    ["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(
      function (t) {
        core.statusBar.image[t].style.display = "block";
      }
    );
    main.statusBar.image.btn8.style.filter = core.getLocalStorage("altKey")
      ? "sepia(1) contrast(1.5)"
      : "";
  } else {
    ["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(
      function (t) {
        core.statusBar.image[t].style.display = "none";
      }
    );
    ["book", "fly", "toolbox", "save", "load", "settings"].forEach(function (
      t
    ) {
      core.statusBar.image[t].style.display = "block";
    });
    core.statusBar.image.keyboard.style.display =
      core.statusBar.image.shop.style.display =
        core.domStyle.isVertical || core.flags.extendToolbar ? "block" : "none";
  }
};

////// ------ resize处理 ------ //

control.prototype._shouldDisplayStatus = function (id) {
  if (id == null) {
    var toDraw = [],
      status = core.dom.status;
    for (var i = 0; i < status.length; ++i) {
      var dom = core.dom.status[i],
        idCol = dom.id;
      if (idCol.indexOf("Col") != idCol.length - 3) continue;
      var id = idCol.substring(0, idCol.length - 3);
      if (!this._shouldDisplayStatus(id)) continue;
      toDraw.push(id);
    }
    return toDraw;
  }
  var obj = {};
  core.flags.statusBarItems.forEach(function (v) {
    obj[v] = true;
  });
  switch (id) {
    case "floor":
      return obj.enableFloor;
    case "name":
      return obj.enableName;
    case "lv":
      return obj.enableLv;
    case "hp":
      return obj.enableHP;
    case "hpmax":
      return obj.enableHPMax;
    case "mana":
      return obj.enableMana;
    case "atk":
      return obj.enableAtk;
    case "def":
      return obj.enableDef;
    case "mdef":
      return obj.enableMDef;
    case "money":
      return obj.enableMoney;
    case "exp":
      return obj.enableExp && !obj.levelUpLeftMode;
    case "up":
      return obj.enableLevelUp;
    case "skill":
      return obj.enableSkill;
    case "key":
      return obj.enableKeys;
    case "pzf":
      return obj.enablePZF;
    case "debuff":
      return obj.enableDebuff;
    default:
      return true;
  }
};

////// 注册一个resize函数 //////
// name为名称,可供注销使用
// func可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。
control.prototype.registerResize = function (name, func) {
  this.unregisterResize(name);
  this.resizes.push({ name: name, func: func });
};

////// 注销一个resize函数 //////
control.prototype.unregisterResize = function (name) {
  this.resizes = this.resizes.filter(function (b) {
    return b.name != name;
  });
};

control.prototype._doResize = function (obj) {
  for (var i in this.resizes) {
    try {
      if (core.doFunc(this.resizes[i].func, this, obj)) return true;
    } catch (e) {
      console.error(e);
      console.error(
        "ERROR in resizes[" + this.resizes[i].name + "]:已自动注销该项。"
      );
      this.unregisterResize(this.resizes[i].name);
    }
  }
  return false;
};

////// 屏幕分辨率改变后重新自适应 //////
control.prototype.resize = function () {
  if (main.mode == "editor") return;
  var clientWidth = main.dom.body.clientWidth,
    clientHeight = main.dom.body.clientHeight;
  var BORDER = 3;
  var extendToolbar = core.flags.extendToolbar;
  let hideLeftStatusBar = core.flags.hideLeftStatusBar;
  var BAR_WIDTH = hideLeftStatusBar ? 0 : Math.round(core._PY_ * 0.31);

  var horizontalMaxRatio =
    (clientHeight - 2 * BORDER - (hideLeftStatusBar ? BORDER : 0)) /
    (core._PY_ + (hideLeftStatusBar ? 38 : 0));

  if (
    clientWidth - 3 * BORDER >= core._PX_ + BAR_WIDTH ||
    (clientWidth > clientHeight && horizontalMaxRatio < 1)
  ) {
    // 横屏
    core.domStyle.isVertical = false;

    core.domStyle.availableScale = [];
    [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5].forEach(function (v) {
      if (
        clientWidth - 3 * BORDER >= v * (core._PX_ + BAR_WIDTH) &&
        horizontalMaxRatio >= v
      ) {
        core.domStyle.availableScale.push(v);
      }
    });
    if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) {
      core.domStyle.scale = Math.min(1, horizontalMaxRatio);
    }
  } else {
    // 竖屏
    core.domStyle.isVertical = true;
    core.domStyle.scale = Math.min((clientWidth - 2 * BORDER) / core._PX_);
    core.domStyle.availableScale = [];
    extendToolbar = false;
    hideLeftStatusBar = false;
    BAR_WIDTH = Math.round(core._PX_ * 0.3);
  }

  var statusDisplayArr = this._shouldDisplayStatus(),
    count = statusDisplayArr.length;
  var statusCanvas = core.flags.statusCanvas,
    statusCanvasRows = core.values.statusCanvasRowsOnMobile || 3;
  var col = statusCanvas ? statusCanvasRows : Math.ceil(count / 3);
  if (col > 5) {
    if (statusCanvas) alert("自绘状态栏的在竖屏下的行数应不超过5!");
    else
      alert(
        "当前状态栏数目(" +
          count +
          ")大于15,请调整到不超过15以避免手机端出现显示问题。"
      );
  }
  var globalAttribute =
    core.status.globalAttribute || core.initStatus.globalAttribute;

  var obj = {
    clientWidth: clientWidth,
    clientHeight: clientHeight,
    BORDER: BORDER,
    BAR_WIDTH: BAR_WIDTH,
    TOOLBAR_HEIGHT: 38,
    outerWidth: core._PX_ * core.domStyle.scale + 2 * BORDER,
    outerHeight: core._PY_ * core.domStyle.scale + 2 * BORDER,
    globalAttribute: globalAttribute,
    border: "3px " + core.arrayToRGBA(globalAttribute.borderColor) + " solid",
    statusDisplayArr: statusDisplayArr,
    count: count,
    col: col,
    statusBarHeightInVertical: core.domStyle.isVertical
      ? (32 * col + 6) * core.domStyle.scale + 2 * BORDER
      : 0,
    toolbarHeightInVertical: core.domStyle.isVertical
      ? 38 * core.domStyle.scale + 2 * BORDER
      : 0,
    extendToolbar: extendToolbar,
    is15x15: false,
    hideLeftStatusBar,
  };

  this._doResize(obj);
  this.setToolbarButton();
  core.updateStatusBar();
};

control.prototype._resize_gameGroup = function (obj) {
  var startBackground = core.domStyle.isVertical
    ? main.styles.startVerticalBackground || main.styles.startBackground
    : main.styles.startBackground;
  if (main.dom.startBackground.getAttribute("__src__") != startBackground) {
    main.dom.startBackground.setAttribute("__src__", startBackground);
    main.dom.startBackground.src = startBackground;
  }

  var gameGroup = core.dom.gameGroup;
  var totalWidth, totalHeight;
  if (core.domStyle.isVertical) {
    totalWidth = obj.outerWidth;
    totalHeight =
      obj.outerHeight +
      obj.statusBarHeightInVertical +
      obj.toolbarHeightInVertical;
  } else {
    totalWidth =
      obj.outerWidth +
      obj.BAR_WIDTH * core.domStyle.scale +
      (obj.hideLeftStatusBar ? 0 : obj.BORDER);
    totalHeight =
      obj.outerHeight +
      (obj.extendToolbar
        ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER
        : 0);
  }
  gameGroup.style.width = totalWidth + "px";
  gameGroup.style.height = totalHeight + "px";
  gameGroup.style.left = (obj.clientWidth - totalWidth) / 2 + "px";
  gameGroup.style.top = (obj.clientHeight - totalHeight) / 2 + "px";
  // floorMsgGroup
  var floorMsgGroup = core.dom.floorMsgGroup;
  floorMsgGroup.style = obj.globalAttribute.floorChangingStyle;
  floorMsgGroup.style.width = obj.outerWidth - 2 * obj.BORDER + "px";
  floorMsgGroup.style.height = totalHeight - 2 * obj.BORDER + "px";
  floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px";
  // startPanel
  core.dom.startPanel.style.fontSize = 16 * core.domStyle.scale + "px";
  // musicBtn
  if (core.domStyle.isVertical || core.domStyle.scale < 1) {
    core.dom.musicBtn.style.right = core.dom.musicBtn.style.bottom = "3px";
  } else {
    core.dom.musicBtn.style.right = (obj.clientWidth - totalWidth) / 2 + "px";
    core.dom.musicBtn.style.bottom =
      (obj.clientHeight - totalHeight) / 2 - 27 + "px";
  }
};

control.prototype._resize_canvas = function (obj) {
  var innerWidth = core._PX_ * core.domStyle.scale + "px",
    innerHeight = core._PY_ * core.domStyle.scale + "px";
  if (!core.isPlaying()) {
    for (var i = 0; i < core.dom.gameCanvas.length; ++i) {
      var ctx = core.dom.gameCanvas[i].getContext("2d");
      core.resizeCanvas(ctx, core._PX_, core._PY_);
      core.dom.gameCanvas[i].style.width = innerWidth;
      core.dom.gameCanvas[i].style.height = innerHeight;
    }
  } else {
    requestAnimationFrame(function () {
      for (var i = 0; i < core.dom.gameCanvas.length; ++i) {
        core.dom.gameCanvas[i].style.width = innerWidth;
        core.dom.gameCanvas[i].style.height = innerHeight;
      }
    });
  }
  core.dom.gif.style.width = innerWidth;
  core.dom.gif.style.height = innerHeight;
  core.dom.gif2.style.width = innerWidth;
  core.dom.gif2.style.height = innerHeight;
  core.dom.gameDraw.style.width = innerWidth;
  core.dom.gameDraw.style.height = innerHeight;
  core.dom.gameDraw.style.top = obj.statusBarHeightInVertical + "px";
  core.dom.gameDraw.style.right = 0;
  core.dom.gameDraw.style.border = obj.border;
  // resize bigmap
  core.bigmap.canvas.forEach(function (cn) {
    var ratio = core.canvas[cn].canvas.hasAttribute("isHD")
      ? core.domStyle.ratio
      : 1;
    core.canvas[cn].canvas.style.width =
      (core.canvas[cn].canvas.width / ratio) * core.domStyle.scale + "px";
    core.canvas[cn].canvas.style.height =
      (core.canvas[cn].canvas.height / ratio) * core.domStyle.scale + "px";
  });
  // resize dynamic canvas
  if (!core.isPlaying()) {
    for (var name in core.dymCanvas) {
      var ctx = core.dymCanvas[name],
        canvas = ctx.canvas;
      // core.maps._setHDCanvasSize(ctx, parseFloat(canvas.getAttribute('_width')), parseFloat(canvas.getAttribute('_height')));
      canvas.style.left =
        parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
      canvas.style.top =
        parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
      var scale = canvas.getAttribute("_scale") || 1;
      core.resizeCanvas(
        canvas,
        (canvas.width * scale) / core.domStyle.scale,
        (canvas.height * scale) / core.domStyle.scale
      );
    }
  } else {
    for (var name in core.dymCanvas) {
      var ctx = core.dymCanvas[name],
        canvas = ctx.canvas;
      core.resizeCanvas(
        ctx,
        parseFloat(canvas.getAttribute("_width")),
        parseFloat(canvas.getAttribute("_height"))
      );
      canvas.style.left =
        parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px";
      canvas.style.top =
        parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px";
    }
  }
  // resize next
  main.dom.next.style.width = main.dom.next.style.height =
    5 * core.domStyle.scale + "px";
  main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth =
    4 * core.domStyle.scale + "px";
};

control.prototype._resize_statusBar = function (obj) {
  // statusBar
  var statusBar = core.dom.statusBar;
  if (core.domStyle.isVertical) {
    statusBar.style.width = obj.outerWidth + "px";
    statusBar.style.height = obj.statusBarHeightInVertical + "px";
    statusBar.style.background = obj.globalAttribute.statusTopBackground;
    statusBar.style.fontSize = 16 * core.domStyle.scale + "px";
  } else {
    statusBar.style.width =
      obj.BAR_WIDTH * core.domStyle.scale + obj.BORDER + "px";
    statusBar.style.height =
      obj.outerHeight +
      (obj.extendToolbar
        ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER
        : 0) +
      "px";
    statusBar.style.background = obj.globalAttribute.statusLeftBackground;
    // --- 计算文字大小
    if (obj.hideLeftStatusBar) {
      statusBar.style.fontSize = 16 * core.domStyle.scale + "px";
    } else {
      statusBar.style.fontSize =
        16 *
          Math.min(1, (core._HEIGHT_ - 4) / obj.count) *
          core.domStyle.scale +
        "px";
    }
  }
  statusBar.style.display = obj.hideLeftStatusBar ? "none" : "block";
  statusBar.style.borderTop = statusBar.style.borderLeft = obj.border;
  statusBar.style.borderRight = core.domStyle.isVertical ? obj.border : "";
  statusBar.style.borderBottom = core.domStyle.isVertical ? "" : obj.border;
  // 自绘状态栏
  if (core.domStyle.isVertical) {
    core.dom.statusCanvas.style.width = core._PX_ * core.domStyle.scale + "px";
    core.dom.statusCanvas.style.height =
      obj.statusBarHeightInVertical - 3 + "px";
    core.maps._setHDCanvasSize(
      core.dom.statusCanvasCtx,
      core._PX_,
      obj.col * 32 + 9
    );
  } else {
    core.dom.statusCanvas.style.width =
      obj.BAR_WIDTH * core.domStyle.scale + "px";
    core.dom.statusCanvas.style.height =
      obj.outerHeight -
      2 * obj.BORDER +
      (obj.extendToolbar
        ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER
        : 0) +
      "px";
    core.maps._setHDCanvasSize(
      core.dom.statusCanvasCtx,
      obj.BAR_WIDTH,
      core._PY_ + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT + obj.BORDER : 0)
    );
  }
  core.dom.statusCanvas.style.display =
    core.flags.statusCanvas && !obj.hideLeftStatusBar ? "block" : "none";
};

control.prototype._resize_status = function (obj) {
  var statusHeight;
  if (core.domStyle.isVertical) {
    statusHeight = 32 * core.domStyle.scale * 0.8;
  } else {
    statusHeight =
      ((obj.hideLeftStatusBar ? core._HEIGHT_ : core._HEIGHT_ - 4) /
        obj.count) *
      32 *
      core.domStyle.scale *
      0.8;
  }
  // status
  for (var i = 0; i < core.dom.status.length; ++i) {
    var id = core.dom.status[i].id,
      style = core.dom.status[i].style;
    if (id.endsWith("Col")) id = id.substring(0, id.length - 3);
    style.display =
      core.flags.statusCanvas || obj.statusDisplayArr.indexOf(id) < 0
        ? "none"
        : "block";
    style.margin = 3 * core.domStyle.scale + "px";
    style.height = statusHeight + "px";
    style.maxWidth =
      obj.BAR_WIDTH *
        core.domStyle.scale *
        (core.domStyle.isVertical ? 0.95 : 1) +
      obj.BORDER +
      "px";
    if (obj.is15x15 && !core.domStyle.isVertical)
      style.marginLeft = 11 * core.domStyle.scale + "px";
  }
  // statusLabels, statusTexts
  for (var i = 0; i < core.dom.statusLabels.length; ++i) {
    core.dom.statusLabels[i].style.lineHeight = statusHeight + "px";
    core.dom.statusLabels[i].style.marginLeft = 6 * core.domStyle.scale + "px";
  }
  for (var i = 0; i < core.dom.statusTexts.length; ++i) {
    core.dom.statusTexts[i].style.color = core.arrayToRGBA(
      obj.globalAttribute.statusBarColor
    );
  }
  // keys
  if (core.flags.statusBarItems.indexOf("enableGreenKey") >= 0) {
    core.dom.keyCol.style.fontSize = "0.75em";
    core.statusBar.greenKey.style.display = "";
  } else {
    core.dom.keyCol.style.fontSize = "";
    core.statusBar.greenKey.style.display = "none";
  }
};

control.prototype._resize_toolBar = function (obj) {
  // toolBar
  var toolBar = core.dom.toolBar;
  if (core.domStyle.isVertical) {
    toolBar.style.left = 0;
    toolBar.style.right = "";
    toolBar.style.width = obj.outerWidth + "px";
    toolBar.style.top = obj.statusBarHeightInVertical + obj.outerHeight + "px";
    toolBar.style.height = obj.toolbarHeightInVertical + "px";
    toolBar.style.background = obj.globalAttribute.toolsBackground;
  } else {
    if (obj.extendToolbar || obj.hideLeftStatusBar) {
      toolBar.style.left = "";
      toolBar.style.right = 0;
      toolBar.style.width = obj.outerWidth + "px";
      toolBar.style.top = obj.outerHeight + "px";
      toolBar.style.height =
        obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER + "px";
      toolBar.style.background = obj.globalAttribute.toolsBackground;
    } else {
      toolBar.style.left = 0;
      toolBar.style.right = "";
      toolBar.style.width =
        obj.BAR_WIDTH * core.domStyle.scale + obj.BORDER + "px";
      toolBar.style.top = 0.75 * obj.outerHeight + "px";
      toolBar.style.height = 0.25 * obj.outerHeight + "px";
      toolBar.style.background = "transparent";
    }
  }
  toolBar.style.borderLeft = obj.border;
  toolBar.style.borderRight = toolBar.style.borderBottom =
    core.domStyle.isVertical || obj.extendToolbar ? obj.border : "";
  toolBar.style.fontSize = 16 * core.domStyle.scale + "px";

  if (
    !core.domStyle.showStatusBar &&
    !core.domStyle.isVertical &&
    !obj.extendToolbar
  ) {
    toolBar.style.display = "none";
  } else {
    toolBar.style.display = "block";
  }
};

control.prototype._resize_tools = function (obj) {
  var toolsHeight =
    32 *
    core.domStyle.scale *
    ((core.domStyle.isVertical || obj.extendToolbar) && !obj.is15x15
      ? 0.95
      : 1);
  var toolsMarginLeft;
  if (core.domStyle.isVertical || obj.extendToolbar)
    toolsMarginLeft = (core._HALF_WIDTH_ - 3) * 3 * core.domStyle.scale;
  else
    toolsMarginLeft =
      (obj.BAR_WIDTH * core.domStyle.scale - 9 - toolsHeight * 3) / 4;
  for (var i = 0; i < core.dom.tools.length; ++i) {
    var style = core.dom.tools[i].style;
    style.height = toolsHeight + "px";
    style.marginLeft = toolsMarginLeft + "px";
    style.marginTop = 3 * core.domStyle.scale + "px";
  }
  core.dom.hard.style.lineHeight = toolsHeight + "px";
  if (core.domStyle.isVertical || obj.extendToolbar) {
    core.dom.hard.style.width =
      obj.outerWidth - 9 * toolsMarginLeft - 8.5 * toolsHeight - 12 + "px";
  } else {
    core.dom.hard.style.width =
      obj.BAR_WIDTH * core.domStyle.scale - 9 - 2 * toolsMarginLeft + "px";
    if (!obj.is15x15) core.dom.hard.style.marginTop = 0;
  }
};