///<reference path="../types/core.d.ts"/>
var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
    init: function () {
        this._afterLoadResources = function () {
            // 本函数将在所有资源加载完毕后，游戏开启前被执行
            // 可以在这个函数里面对资源进行一些操作。
            // 若需要进行切分图片，可以使用 core.splitImage() 函数，或直接在全塔属性-图片切分中操作
            //
            core.ui.statusBar.init();
            console.clear();
            console.log(`当你逃出 Magic 4 的牢笼后，你来到了这里`);
            console.log(`这里与 50 层魔塔非常相似，但是似乎没这么简单`);
            console.log(`或许你应该去寻找下在哪能获得黄钥匙`);
            console.log(`一些必要信息：`);
            console.log(`本游戏完全开源，你可以随意查看代码`);
            console.log(`所有的代码修改已经在 change 插件中说明`);
        };

        // 可以在任何地方（如afterXXX或自定义脚本事件）调用函数，方法为 core.plugin.xxx();
        // 从V2.6开始，插件中用this.XXX方式定义的函数也会被转发到core中，详见文档-脚本-函数的转发。
    },
    resizeTo11: function () {
        if (main.mode == 'editor') {
            return;
        }

        // 在不修改libs的情况下将页面适配为11x11
        core.__SIZE__ = 11;
        core.__PIXELS__ = core.__SIZE__ * 32;
        core.__HALF_SIZE__ = 5;
        // core.bigmap.width = core.__SIZE__;
        // core.bigmap.height = core.__SIZE__;
        core.actions.SIZE = core.__SIZE__;
        core.actions.HSIZE = core.__HALF_SIZE__;
        core.actions.LAST = core.__SIZE__ - 1;
        core.actions.CHOICES_LEFT = 3;
        core.actions.CHOICES_RIGHT =
            core.actions.LAST - core.actions.CHOICES_LEFT;
        core.ui.SIZE = core.__SIZE__;
        core.ui.HSIZE = core.__HALF_SIZE__;
        core.ui.LAST = core.__SIZE__ - 1;
        core.ui.PIXEL = core.__PIXELS__;
        core.ui.HPIXEL = core.__PIXELS__ / 2;
    },
    statusBar: function () {
        main.dom.floorMsgGroup.style.display = 'none';
        main.dom.statusBar.style.display = 'none';
        main.dom.toolBar.style.display = 'none';

        const GAMEVIEW_WIDTH = 640;
        const GAMEVIEW_HEIGHT = 422;

        const GAMEVIEW_WIDTH_VERTICAL = 388;
        const GAMEVIEW_HEIGHT_VERTICAL = 630;

        const BAR_WIDTH = 125;
        const BAR_HEIGHT_VERTICAL = 130;
        const BORDER_WIDTH = 18;
        const BORDER_HEIGHT = 24;

        const ITEM_BOX_LEFT = 13;
        const ITEM_BOX_TOP = 190;
        const ITEM_BOX_LEFT_VERTICAL = 219;
        const ITEM_BOX_TOP_VERTICAL = 10;

        const ITEM_ICON_OUTER_SIZE = 33;

        const EQUIP_BLOCK_LEFT = 525;
        const EQUIP_BLOCK_TOP = 40;
        const EQUIP_BLOCK_LEFT_VERTICAL = 108;
        const EQUIP_BLOCK_TOP_VERTICAL = 0;

        const KEY_BLOCK_LEFT = EQUIP_BLOCK_LEFT;
        const KEY_BLOCK_LEFT_VERTICAL = EQUIP_BLOCK_LEFT_VERTICAL;
        const KEY_BLOCK_TOP_VERTICAL = 86;

        const INFO_BLOCK_LEFT = EQUIP_BLOCK_LEFT;
        const INFO_BLOCK_TOP = 220;
        const INFO_BLOCK_LEFT_VERTICAL = 8;
        const INFO_BLOCK_TOP_VERTICAL = 538;

        const TOOL_BOX_LEFT = EQUIP_BLOCK_LEFT;
        const TOOL_BOX_TOP = 314;
        const TOOL_BOX_LEFT_VERTICAL = 278;
        const TOOL_BOX_TOP_VERTICAL = INFO_BLOCK_TOP_VERTICAL;

        const TOOL_ICON_OUTER_SIZE = 34;

        const INFO_BAR_HEIGHT = 22;
        const INFO_BAR_HEIGHT_VERTICAL = 18;
        const INFO_BAR_TOP = GAMEVIEW_HEIGHT - INFO_BAR_HEIGHT;
        const INFO_BAR_TOP_VERTICAL =
            GAMEVIEW_HEIGHT_VERTICAL - INFO_BAR_HEIGHT_VERTICAL;

        const TEXT_COLOR = '#E1E1E1';

        const FORCE_COUNTABLE_ITEMS = ['centerFly'];

        const outerBackground = document.createElement('canvas');
        outerBackground.style.position = 'absolute';
        outerBackground.style.zIndex = 5;
        outerBackground.id = 'outerBackground';
        main.dom.outerBackground = outerBackground;
        main.dom.startPanel.insertAdjacentElement('afterend', outerBackground);

        const outerUI = document.createElement('canvas');
        outerUI.style.position = 'absolute';
        outerUI.style.zIndex = 165;
        outerUI.id = 'outerUI';
        main.dom.outerUI = outerUI;
        outerBackground.insertAdjacentElement('afterend', outerUI);
        setTimeout(function () {
            // Should be executed immediately after init()
            main.canvas.outerUI = outerUI.getContext('2d');
        });
        outerUI.onclick = function (e) {
            try {
                e.preventDefault();
                if (!core.isPlaying()) return false;
                const left = core.dom.gameGroup.offsetLeft;
                const top = core.dom.gameGroup.offsetTop;
                const px = parseInt((e.clientX - left) / core.domStyle.scale),
                    py = parseInt((e.clientY - top) / core.domStyle.scale);
                core.ui.statusBar.onclick(px, py);
            } catch (ee) {
                main.log(ee);
            }
        };

        const _resize_gameGroup = function (obj) {
            const gameGroup = core.dom.gameGroup;
            gameGroup.style.width = obj.totalWidth + 'px';
            gameGroup.style.height = obj.totalHeight + 'px';
            gameGroup.style.left =
                (obj.clientWidth - obj.totalWidth) / 2 + 'px';
            gameGroup.style.top =
                (obj.clientHeight - obj.totalHeight) / 2 + 'px';

            core.dom.musicBtn.style.right =
                (obj.clientWidth - obj.totalWidth) / 2 + 'px';
            core.dom.musicBtn.style.bottom =
                (obj.clientHeight - obj.totalHeight) / 2 - 27 + 'px';
            core.dom.enlargeBtn.style.right =
                (obj.clientWidth - obj.totalWidth) / 2 + 31 + 'px';
            core.dom.enlargeBtn.style.bottom =
                (obj.clientHeight - obj.totalHeight) / 2 - 27 + 'px';

            main.dom.startBackground.src = main.styles.startBackground;
        };

        const _resize_canvas = function (obj) {
            main.dom.outerBackground.style.width = obj.totalWidth + 'px';
            main.dom.outerBackground.style.height = obj.totalHeight + 'px';
            main.dom.outerUI.style.width = obj.totalWidth + 'px';
            main.dom.outerUI.style.height = obj.totalHeight + 'px';

            // perf 8: HD compatibility.
            const innerSize = obj.canvasWidth * core.domStyle.scale + 'px';
            const ratio = core.domStyle.scale * devicePixelRatio;
            const hdSize = obj.canvasWidth * ratio;
            for (let i = 0; i < core.dom.gameCanvas.length; ++i) {
                core.dom.gameCanvas[i].style.width = innerSize;
                core.dom.gameCanvas[i].style.height = innerSize;
                if (!core.dom.gameCanvas[i].classList.contains('no-anti')) {
                    core.dom.gameCanvas[i].width = hdSize;
                    core.dom.gameCanvas[i].height = hdSize;
                    core.dom.gameCanvas[i].getContext('2d').scale(ratio, ratio);
                }
            }
            core.dom.gif.style.width = core.dom.gif.style.height = innerSize;
            core.dom.gif2.style.width = core.dom.gif2.style.height = innerSize;
            core.dom.gameDraw.style.width = core.dom.gameDraw.style.height =
                innerSize;
            core.dom.gameDraw.style.top =
                obj.gameDrawBox.top * core.domStyle.scale + 'px';
            core.dom.gameDraw.style.left =
                obj.gameDrawBox.left * core.domStyle.scale + 'px';

            // resize bigmap
            core.bigmap.canvas.forEach(function (cn) {
                const 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
            for (const name in core.dymCanvas) {
                const ctx = core.dymCanvas[name],
                    canvas = ctx.canvas;
                const ratio = canvas.hasAttribute('isHD')
                    ? core.domStyle.ratio
                    : 1;
                canvas.style.width =
                    (canvas.width / ratio) * core.domStyle.scale + 'px';
                canvas.style.height =
                    (canvas.height / ratio) * core.domStyle.scale + 'px';
                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';
        };

        core.control.resize = function () {
            if (main.mode == 'editor') return;

            const clientWidth = main.dom.body.clientWidth,
                clientHeight = main.dom.body.clientHeight;
            const canvasWidth = core.__PIXELS__;

            const isVertical = clientHeight > clientWidth;
            core.domStyle.isVertical = isVertical;

            const totalWidth = isVertical
                    ? GAMEVIEW_WIDTH_VERTICAL
                    : GAMEVIEW_WIDTH,
                totalHeight = isVertical
                    ? GAMEVIEW_HEIGHT_VERTICAL
                    : GAMEVIEW_HEIGHT;

            const maxRatio = Math.min(
                clientWidth / totalWidth,
                clientHeight / totalHeight
            );

            core.domStyle.availableScale = [];
            [1, 1.25, 1.5, 1.75, 2].forEach(function (v) {
                if (maxRatio >= v) {
                    core.domStyle.availableScale.push(v);
                }
            });

            if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) {
                core.domStyle.scale = Math.min(1, maxRatio);
            } else if (
                core.getLocalStorage('scale') == null &&
                core.domStyle.availableScale.length >= 2
            ) {
                core.domStyle.scale =
                    core.domStyle.availableScale[
                        core.domStyle.availableScale.length - 2
                    ];
                core.setLocalStorage('scale', core.domStyle.scale);
            }

            const totalWidthScaled = totalWidth * core.domStyle.scale,
                totalHeightScaled = totalHeight * core.domStyle.scale;

            const gameDrawBox = isVertical
                ? {
                      left: BORDER_WIDTH,
                      top: BAR_HEIGHT_VERTICAL + BORDER_HEIGHT
                  }
                : { left: BAR_WIDTH + BORDER_WIDTH, top: BORDER_HEIGHT };

            const obj = {
                clientWidth: clientWidth,
                clientHeight: clientHeight,
                canvasWidth: canvasWidth,
                totalWidth: totalWidthScaled,
                totalHeight: totalHeightScaled,
                gameDrawBox: gameDrawBox,
                globalAttribute:
                    core.status.globalAttribute ||
                    core.initStatus.globalAttribute
            };

            _resize_gameGroup(obj);
            _resize_canvas(obj);

            if (core.status.automaticRoute == null)
                core.status.automaticRoute = {};
            core.control.setViewport(32, 32);
            core.updateStatusBar();
        };

        class StatusBar {
            constructor() {
                this.itemMx = [
                    ['book', 'wand', 'fly'],
                    ['cross', 'superPotion', 'pickaxe'],
                    ['bomb', 'centerFly', 'upFly'],
                    ['downFly', 'knife', 'snow'],
                    ['bigKey', 'earthquake', 'coin']
                ];
            }
            init() {
                this.toolbarAction = [
                    [
                        main.core.openKeyBoard,
                        main.core.openQuickShop,
                        function () {
                            core.insertAction([
                                { type: 'insert', name: '游戏说明' }
                            ]);
                        }
                    ],
                    [main.core.save, main.core.load, main.core.openSettings]
                ];
                this.replayAction = [
                    [core.triggerReplay, core.stopReplay, core.rewindReplay],
                    [core.speedDownReplay, core.speedUpReplay, core.saveReplay]
                ];
            }
            update() {
                this._update_background();
                this._update_props();
                this._update_items();
                this._update_equips();
                this._update_keys();
                this._update_infoWindow();
                this._update_infoBar();
            }
            _update_background(updatedFloorTitle) {
                const bgctx = main.dom.outerBackground.getContext('2d');
                const uictx = main.dom.outerUI.getContext('2d');

                // perf 3: HD compatibility.

                const ratio = core.domStyle.scale * devicePixelRatio;
                if (core.domStyle.isVertical) {
                    bgctx.canvas.width = GAMEVIEW_WIDTH_VERTICAL * ratio;
                    bgctx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL * ratio;
                    uictx.canvas.width = GAMEVIEW_WIDTH_VERTICAL * ratio;
                    uictx.canvas.height = GAMEVIEW_HEIGHT_VERTICAL * ratio;
                    bgctx.scale(ratio, ratio);
                    bgctx.imageSmoothingEnabled = false;
                    uictx.scale(ratio, ratio);
                    const bg =
                        core.material.images.images[
                            'statusBackground_vertical.png'
                        ];
                    bgctx.drawImage(
                        bg,
                        0,
                        0,
                        GAMEVIEW_WIDTH_VERTICAL,
                        GAMEVIEW_HEIGHT_VERTICAL
                    );
                    core.setTextAlign('outerUI', 'center');
                } else {
                    bgctx.canvas.width = GAMEVIEW_WIDTH * ratio;
                    bgctx.canvas.height = GAMEVIEW_HEIGHT * ratio;
                    uictx.canvas.width = GAMEVIEW_WIDTH * ratio;
                    uictx.canvas.height = GAMEVIEW_HEIGHT * ratio;
                    if (!main.replayChecking) {
                        bgctx.scale(ratio, ratio);
                        bgctx.imageSmoothingEnabled = false;
                        uictx.scale(ratio, ratio);
                    }
                    const bg =
                        core.material.images.images['statusBackground.png'];
                    bgctx.drawImage(
                        bg,
                        0,
                        0,
                        GAMEVIEW_WIDTH,
                        GAMEVIEW_HEIGHT - INFO_BAR_HEIGHT
                    );
                    bgctx.fillStyle = '#676767';
                    bgctx.fillRect(
                        0,
                        INFO_BAR_TOP,
                        GAMEVIEW_WIDTH,
                        INFO_BAR_HEIGHT
                    );
                    core.setTextAlign('outerUI', 'center');
                }
            }
            // 更新属性
            _update_props(updatedFloorTitle) {
                if (!updatedFloorTitle && core.status.floorId) {
                    updatedFloorTitle =
                        core.status.maps[core.status.floorId].title;
                }
                const statusList = ['hp', 'atk', 'def', 'money'];
                const drawStatusList = (baseX, baseY) => {
                    let curh = baseY;
                    core.setTextAlign('outerUI', 'right');
                    let i = 0;
                    statusList.forEach(item => {
                        // 四舍五入
                        core.status.hero[item] = Math.round(
                            core.status.hero[item]
                        );
                        // 大数据格式化
                        core.fillText(
                            'outerUI',
                            core.getRealStatus(item),
                            baseX,
                            curh,
                            TEXT_COLOR
                        );
                        // perf 10: New background UI compatibility.
                        curh += i === 0 ? 25 : 23.5;
                        i++;
                    });
                    core.setTextAlign('outerUI', 'center');
                };
                if (core.domStyle.isVertical) {
                    core.clearMap('outerUI', 10, 0, 105, 120);
                    core.setFont('outerUI', '14px Verdana');
                    if (updatedFloorTitle) {
                        core.fillText(
                            'outerUI',
                            updatedFloorTitle,
                            54,
                            22,
                            TEXT_COLOR
                        );
                    }
                    drawStatusList(96, 46);
                } else {
                    core.clearMap('outerUI', 10, 40, 105, 130);
                    core.setFont('outerUI', '16px Verdana');
                    if (updatedFloorTitle) {
                        // perf 12: Change title.
                        core.fillText('outerUI', 'Magic', 64, 37, TEXT_COLOR);
                        core.fillText(
                            'outerUI',
                            updatedFloorTitle,
                            64,
                            59,
                            TEXT_COLOR
                        );
                    }
                    drawStatusList(110, 93.5);
                }
            }
            _update_items() {
                const drawItemMx = drawFn => {
                    for (let i = 0; i < this.itemMx.length; i++) {
                        for (let j = 0; j < this.itemMx[i].length; j++) {
                            var item = this.itemMx[i][j];
                            drawFn(i, j, item);
                        }
                    }
                };
                const drawItem = (item, posx, posy) => {
                    const icon = core.material.icons.items[item],
                        image = core.material.images.items;
                    core.drawImage(
                        'outerUI',
                        image,
                        0,
                        32 * icon,
                        32,
                        32,
                        posx,
                        posy,
                        32,
                        32
                    );
                    const cnt = core.itemCount(item);
                    if (
                        (core.items.items[item].cls === 'tools' && cnt > 1) ||
                        FORCE_COUNTABLE_ITEMS.includes(item)
                    ) {
                        core.fillText(
                            'outerUI',
                            cnt,
                            posx + 25,
                            posy + 28,
                            '#FFFFFF',
                            'bold 12px Verdana'
                        );
                    }
                    // if (this.selectId == item)
                    //     core.strokeRect('outerUI', posx + 17, posy - 4, 40, 40, '#FFD700');
                };

                // perf 4: Enhanced pixelated rendering.
                core.canvas.outerUI.imageSmoothingEnabled = false;
                if (core.domStyle.isVertical) {
                    core.clearMap(
                        'outerUI',
                        ITEM_BOX_LEFT_VERTICAL,
                        ITEM_BOX_TOP_VERTICAL,
                        105,
                        185
                    );
                    drawItemMx((i, j, item) => {
                        if (core.hasItem(item)) {
                            const posx = ITEM_BOX_LEFT_VERTICAL + i * 33,
                                posy = ITEM_BOX_TOP_VERTICAL + j * 32;
                            drawItem(item, posx, posy);
                        }
                    });
                } else {
                    core.clearMap(
                        'outerUI',
                        ITEM_BOX_LEFT,
                        ITEM_BOX_TOP,
                        105,
                        185
                    );
                    drawItemMx((i, j, item) => {
                        if (core.hasItem(item)) {
                            const posx = ITEM_BOX_LEFT + j * 33,
                                posy = ITEM_BOX_TOP + i * 32;
                            drawItem(item, posx, posy);
                        }
                    });
                }
                core.canvas.outerUI.imageSmoothingEnabled = true;
            }
            _update_equips() {
                core.setFont('outerUI', 'bold 16px Verdana');
                const drawEquip = (baseX, baseY, id, color, back) => {
                    if (!id)
                        core.fillText(
                            'outerUI',
                            back,
                            baseX + 50,
                            baseY + 22,
                            color
                        );
                    else {
                        core.fillText(
                            'outerUI',
                            core.material.items[id].name,
                            baseX + 32,
                            baseY + 22,
                            color
                        );
                        var icon = core.material.icons.items[id];
                        // perf 5: Enhanced pixelated rendering.
                        core.canvas.outerUI.imageSmoothingEnabled = false;
                        core.drawImage(
                            'outerUI',
                            core.material.images.items,
                            0,
                            32 * icon,
                            32,
                            32,
                            baseX + 64,
                            baseY,
                            32,
                            32
                        );
                        core.canvas.outerUI.imageSmoothingEnabled = true;
                    }
                };
                if (core.domStyle.isVertical) {
                    core.clearMap(
                        'outerUI',
                        EQUIP_BLOCK_LEFT_VERTICAL,
                        EQUIP_BLOCK_TOP_VERTICAL,
                        105,
                        95
                    );
                    drawEquip(
                        EQUIP_BLOCK_LEFT_VERTICAL,
                        EQUIP_BLOCK_TOP_VERTICAL + 9,
                        core.getFlag('nowWeapon'),
                        '#FFCFAE',
                        '无武器'
                    );
                    drawEquip(
                        EQUIP_BLOCK_LEFT_VERTICAL,
                        EQUIP_BLOCK_TOP_VERTICAL + 49,
                        core.getFlag('nowShield'),
                        '#D1CEFF',
                        '无防具'
                    );
                } else {
                    core.clearMap(
                        'outerUI',
                        EQUIP_BLOCK_LEFT,
                        EQUIP_BLOCK_TOP,
                        105,
                        95
                    );
                    // perf 9: New background UI compatibility.
                    drawEquip(
                        EQUIP_BLOCK_LEFT,
                        EQUIP_BLOCK_TOP - 4,
                        core.getFlag('nowWeapon'),
                        '#FFCFAE',
                        '无武器'
                    );
                    drawEquip(
                        EQUIP_BLOCK_LEFT,
                        EQUIP_BLOCK_TOP + 44,
                        core.getFlag('nowShield'),
                        '#D1CEFF',
                        '无防具'
                    );
                }
            }
            _update_keys() {
                const drawKeyList = (baseX, baseY, lines, rows) => {
                    const todraw = [],
                        keyList = ['yellowKey', 'blueKey', 'redKey'];
                    let total = 0;
                    keyList.forEach(function (key, i) {
                        todraw[i] = core.itemCount(key);
                        total += todraw[i];
                    });
                    if (total > lines * rows) {
                        let dn = 2;
                        for (let i = 0; i <= dn; i++) {
                            const deltaX = 0;
                            const deltaY = 48 - i * 22;
                            // perf 6: Enhanced pixelated rendering.
                            core.canvas.outerUI.imageSmoothingEnabled = false;
                            this.drawKey(
                                keyList[i],
                                baseX + deltaX,
                                baseY + deltaY
                            );
                            core.canvas.outerUI.imageSmoothingEnabled = true;
                            core.setFont('outerUI', '14px Verdana');
                            core.setTextAlign('outerUI', 'left');
                            core.canvas.outerUI.textBaseline = 'middle';
                            core.fillText(
                                'outerUI',
                                todraw[i],
                                baseX + deltaX + 16,
                                baseY + deltaY + 9,
                                TEXT_COLOR
                            );
                        }
                    } else {
                        let dn = 2,
                            dc = 0;
                        // perf 7: Enhanced pixelated rendering.
                        core.canvas.outerUI.imageSmoothingEnabled = false;
                        while (dn >= 0 && dc < lines * rows) {
                            if (todraw[dn]) {
                                this.drawKey(
                                    keyList[dn],
                                    baseX + (dc % rows) * 14,
                                    baseY + parseInt(dc / rows) * 17
                                );
                                todraw[dn]--, dc++;
                            } else dn--;
                        }
                        core.canvas.outerUI.imageSmoothingEnabled = true;
                    }
                };
                if (core.domStyle.isVertical) {
                    core.clearMap(
                        'outerUI',
                        KEY_BLOCK_LEFT_VERTICAL,
                        KEY_BLOCK_TOP_VERTICAL,
                        105,
                        75
                    );
                    drawKeyList(
                        KEY_BLOCK_LEFT_VERTICAL + 3,
                        KEY_BLOCK_TOP_VERTICAL + 2,
                        2,
                        7
                    );
                } else {
                    core.clearMap('outerUI', KEY_BLOCK_LEFT, 140, 105, 75);
                    drawKeyList(KEY_BLOCK_LEFT + 3, 142, 3, 7);
                }
            }
            drawKey(key, x, y) {
                let sx = 3,
                    sy = 0;
                if (key == 'blueKey') sx += 16;
                else if (key == 'yellowKey') sy += 16;
                core.drawImage(
                    'outerUI',
                    core.statusBar.icons.keys,
                    sx,
                    sy,
                    10,
                    16,
                    x,
                    y,
                    10,
                    16
                );
            }
            _update_infoWindow() {
                const itemId = this.selectedItem;
                if (core.domStyle.isVertical) {
                    core.clearMap(
                        'outerUI',
                        INFO_BLOCK_LEFT_VERTICAL,
                        INFO_BLOCK_TOP_VERTICAL,
                        260,
                        64
                    );
                    if (this.selectedItem) {
                        const icon = core.material.icons.items[itemId];
                        core.fillText(
                            'outerUI',
                            core.material.items[itemId].name,
                            INFO_BLOCK_LEFT_VERTICAL + 32,
                            INFO_BLOCK_TOP_VERTICAL + 35,
                            '#D1CEFF'
                        );
                        core.drawImage(
                            'outerUI',
                            core.material.images.items,
                            0,
                            32 * icon,
                            32,
                            32,
                            INFO_BLOCK_LEFT_VERTICAL + 64,
                            INFO_BLOCK_TOP_VERTICAL + 15,
                            32,
                            32
                        );
                        core.ui.drawTextContent(
                            'outerUI',
                            core.material.items[itemId].text,
                            {
                                left: INFO_BLOCK_LEFT_VERTICAL + 100,
                                top: INFO_BLOCK_TOP_VERTICAL + 3,
                                maxWidth: 160,
                                color: '#D1CEFF'
                            }
                        );
                    }
                } else {
                    core.clearMap(
                        'outerUI',
                        INFO_BLOCK_LEFT,
                        INFO_BLOCK_TOP,
                        105,
                        94
                    );
                    if (this.selectedItem) {
                        const icon = core.material.icons.items[itemId];
                        core.fillText(
                            'outerUI',
                            core.material.items[itemId].name,
                            INFO_BLOCK_LEFT + 32,
                            INFO_BLOCK_TOP + 25,
                            '#D1CEFF'
                        );
                        core.drawImage(
                            'outerUI',
                            core.material.images.items,
                            0,
                            32 * icon,
                            32,
                            32,
                            INFO_BLOCK_LEFT + 64,
                            INFO_BLOCK_TOP + 4,
                            32,
                            32
                        );
                        core.ui.drawTextContent(
                            'outerUI',
                            core.material.items[itemId].text,
                            {
                                left: INFO_BLOCK_LEFT + 1,
                                top: INFO_BLOCK_TOP + 36,
                                maxWidth: 105,
                                color: '#D1CEFF'
                            }
                        );
                    }
                }
            }
            showItemInfo(itemId) {
                this.selectedItem = itemId;
                this._update_infoWindow();
            }
            clearItemInfo() {
                this.selectedItem = null;
                this._update_infoWindow();
            }
            _update_toolBox() {
                const tools = core.isReplaying()
                    ? [
                          [
                              core.status.replay.pausing ? 'play' : 'pause',
                              'stop',
                              'rewind'
                          ],
                          ['speedDown', 'speedUp', 'save']
                      ]
                    : [
                          ['keyboard', 'shop', 'help'],
                          ['save', 'load', 'settings']
                      ];
                // perf 11: Enhanced pixelated rendering.
                core.canvas.outerUI.imageSmoothingEnabled = false;
                if (core.domStyle.isVertical) {
                    core.clearMap(
                        'outerUI',
                        TOOL_BOX_LEFT_VERTICAL,
                        TOOL_BOX_TOP_VERTICAL,
                        115,
                        80
                    );
                    for (let i = 0; i < tools.length; i++) {
                        for (let j = 0; j < tools[i].length; j++) {
                            core.drawIcon(
                                'outerUI',
                                tools[i][j],
                                TOOL_BOX_LEFT_VERTICAL + j * 34,
                                TOOL_BOX_TOP_VERTICAL + i * 34,
                                32,
                                32
                            );
                        }
                    }
                } else {
                    core.clearMap(
                        'outerUI',
                        TOOL_BOX_LEFT,
                        TOOL_BOX_TOP,
                        115,
                        80
                    );
                    for (let i = 0; i < tools.length; i++) {
                        for (let j = 0; j < tools[i].length; j++) {
                            core.drawIcon(
                                'outerUI',
                                tools[i][j],
                                TOOL_BOX_LEFT + j * 34,
                                TOOL_BOX_TOP + i * 34,
                                32,
                                32
                            );
                        }
                    }
                }
                core.canvas.outerUI.imageSmoothingEnabled = true;
            }
            onclick(x, y) {
                const makeBox = ([x, y], [w, h]) => {
                    return [
                        [x, y],
                        [x + w, y + h]
                    ];
                };
                const gridify = ([x, y], [gw, gh]) => {
                    return [parseInt(x / gw), parseInt(y / gh)];
                };
                const useItem = itemId => {
                    if (!core.hasItem(itemId)) return;
                    if (core.material.items[itemId].cls == 'constants') {
                        switch (itemId) {
                            case 'book':
                                core.openBook(true);
                                break;
                            case 'fly':
                                core.useFly(true);
                                break;
                            case 'wand':
                                core.insertAction({
                                    type: 'useItem',
                                    id: itemId
                                });
                                break;
                            case 'snow':
                                core.useItem('snow');
                                break;
                            default:
                                this.showItemInfo(itemId);
                        }
                    } else if (itemId != this.selectedItem) {
                        this.showItemInfo(itemId);
                    } else {
                        switch (itemId) {
                            case 'centerFly':
                                core.ui._drawCenterFly();
                                break;
                            default:
                                core.useItem(itemId);
                        }
                    }
                };
                const inRect = ([x, y], [[sx, sy], [dx, dy]]) => {
                    return sx <= x && x <= dx && sy <= y && y <= dy;
                };
                const relativeTo = ([x, y], [ax, ay]) => {
                    return [x - ax, y - ay];
                };
                const pos = [x, y];
                if (core.domStyle.isVertical) {
                    const itemBox = makeBox(
                        [ITEM_BOX_LEFT_VERTICAL, ITEM_BOX_TOP_VERTICAL],
                        [32 * 5, 33 * 3]
                    );
                    if (inRect(pos, itemBox)) {
                        if (
                            core.isReplaying() ||
                            core.status.lockControl ||
                            core.isMoving()
                        )
                            return;
                        const [gx, gy] = gridify(
                            relativeTo(pos, itemBox[0]),
                            [32, 33]
                        );
                        const itemId = this.itemMx[gx][gy];
                        useItem(itemId);
                        return;
                    }
                    const toolBox = makeBox(
                        [TOOL_BOX_LEFT_VERTICAL, TOOL_BOX_TOP_VERTICAL],
                        [34 * 3, 34 * 2]
                    );
                    if (inRect(pos, toolBox)) {
                        const [row, col] = gridify(
                            relativeTo(pos, toolBox[0]),
                            [34, 34]
                        );
                        if (core.isReplaying()) {
                            this.replayAction[col][row].call(core);
                        } else if (core.isPlaying()) {
                            this.toolbarAction[col][row].call(core, true);
                        }
                        return;
                    }
                } else {
                    const itemBox = makeBox(
                        [ITEM_BOX_LEFT, ITEM_BOX_TOP],
                        [33 * 3, 32 * 5]
                    );
                    if (inRect(pos, itemBox)) {
                        if (
                            core.isReplaying() ||
                            core.status.lockControl ||
                            core.isMoving()
                        )
                            return;
                        const [gx, gy] = gridify(
                            relativeTo(pos, itemBox[0]),
                            [33, 32]
                        );
                        const itemId = this.itemMx[gy][gx];
                        useItem(itemId);
                        return;
                    }
                    const toolBox = makeBox(
                        [TOOL_BOX_LEFT, TOOL_BOX_TOP],
                        [34 * 3, 34 * 2]
                    );
                    if (inRect(pos, toolBox)) {
                        const [row, col] = gridify(
                            relativeTo(pos, toolBox[0]),
                            [34, 34]
                        );
                        if (core.isReplaying()) {
                            this.replayAction[col][row].call(core);
                        } else if (core.isPlaying()) {
                            this.toolbarAction[col][row].call(core, true);
                        }
                        return;
                    }
                }
            }
            infoText;
            infocnt = 0;
            _update_infoBar() {
                core.setTextAlign('outerUI', 'left');
                const text = this.infoText;
                if (core.domStyle.isVertical) {
                    core.clearMap(
                        'outerUI',
                        0,
                        INFO_BAR_TOP_VERTICAL,
                        GAMEVIEW_WIDTH,
                        INFO_BAR_HEIGHT_VERTICAL
                    );
                    core.setFont('outerUI', 'bold 14px Verdana');
                    if (text) {
                        core.fillText(
                            'outerUI',
                            text,
                            10,
                            INFO_BAR_TOP_VERTICAL + 14,
                            TEXT_COLOR
                        );
                    }
                } else {
                    core.clearMap(
                        'outerUI',
                        0,
                        INFO_BAR_TOP,
                        GAMEVIEW_WIDTH,
                        INFO_BAR_HEIGHT
                    );
                    core.setFont('outerUI', 'bold 16px Verdana');
                    if (text) {
                        core.fillText(
                            'outerUI',
                            text,
                            10,
                            INFO_BAR_TOP + 16,
                            TEXT_COLOR
                        );
                    }
                }
                core.setTextAlign('outerUI', 'center');
            }
            print(text, cnt = 1) {
                this.infoText = text;
                this.infocnt = cnt;
                this._update_infoBar();
            }
            static infoRules = [
                { id: 'lava', text: '岩浆好热啊!' },
                { id: 'upFloor', text: '你看到了楼梯' },
                { id: 'downFloor', text: '你看到了楼梯' },
                { id: 'blueShop', text: '你看到了一个祭坛' },
                { id: 'man', text: '你看到了一个老人' },
                { id: 'woman', text: '你看到了一个商人' },
                { id: 'fairy', text: '你看到了一个商人' },
                { id: 'thief', text: '你看到了一个小偷' }
            ];
            printEnvironmentInfo() {
                if (!this.infocnt) {
                    return;
                }
                const ids = [];
                for (const block of core.status.thisMap.blocks) {
                    if (!block.disable && core.nearHero(block.x, block.y))
                        ids.push(block.event.id);
                }
                for (const infoRule of StatusBar.infoRules) {
                    if (ids.indexOf(infoRule.id) >= 0) {
                        this.print(infoRule.text);
                        return;
                    }
                }
            }
            clearInfo(etype) {
                this.clearItemInfo();
                if (this.infocnt == 1) {
                    setTimeout(() => {
                        if (this.infocnt === 0) {
                            this.infoText = void 0;
                            this._update_infoBar();
                        }
                    }, 200);
                    this.infocnt = 0;
                } else if (this.infocnt > 1) {
                    this.infocnt--;
                }
            }
        }

        core.ui.statusBar = new StatusBar();

        core.control.clearStatusBar = function () {
            core.clearMap('outerUI');
        };
        // init() called in `afterLoadResources`.
    },
    override: function () {
        core.statusBar.icons = {
            floor: 0,
            name: null,
            lv: 1,
            hpmax: 2,
            hp: 3,
            atk: 4,
            def: 5,
            mdef: 6,
            money: 7,
            experience: 8,
            up: 9,
            book: 10,
            fly: 11,
            toolbox: 12,
            keyboard: 13,
            shop: 14,
            save: 15,
            load: 16,
            settings: 17,
            play: 18,
            pause: 19,
            stop: 20,
            speedDown: 21,
            speedUp: 22,
            rewind: 23,
            equipbox: 24,
            mana: 25,
            skill: 26,
            paint: 27,
            erase: 28,
            empty: 29,
            exit: 30,
            btn1: 31,
            btn2: 32,
            btn3: 33,
            btn4: 34,
            btn5: 35,
            btn6: 36,
            btn7: 37,
            btn8: 38,
            keys: 39,
            help: 40,
            battle: 41
        };

        core.actions._clickCenterFly = function (x, y) {
            var posX = core.status.event.data.posX,
                posY = core.status.event.data.posY;
            core.ui.closePanel();
            if (x == posX && y == posY) {
                if (core.canUseItem('centerFly')) {
                    core.useItem('centerFly');
                } else {
                    core.drawTip('当前不能使用中心对称飞行器');
                }
            } else core.drawTip('取消使用');
        };

        var _clickSL = core.actions._clickSL;
        core.actions._clickSL = function (x, y) {
            var page = core.status.event.data.page,
                offset = core.status.event.data.offset;
            var index = page * 10 + offset;

            // 上一页
            if (
                (x == this.HSIZE - 1 || x == this.HSIZE - 2) &&
                y == this.LAST
            ) {
                core.ui._drawSLPanel(10 * (page - 1) + offset);
                return;
            }
            // 下一页
            if (
                (x == this.HSIZE + 1 || x == this.HSIZE + 2) &&
                y == this.LAST
            ) {
                core.ui._drawSLPanel(10 * (page + 1) + offset);
                return;
            }
            if (
                (x == this.HSIZE - 3 || x == this.HSIZE + 3) &&
                y == this.LAST
            ) {
                return;
            }
            _clickSL.call(this, x, y);
        };

        var _sys_longClick_lockControl =
            core.actions._sys_longClick_lockControl;
        core.actions._sys_longClick_lockControl = function (x, y) {
            if (!core.status.lockControl) return false;

            // 长按SL上下页快速翻页
            if (
                ['save', 'load', 'replayLoad', 'replayRemain'].indexOf(
                    core.status.event.id
                ) >= 0
            ) {
                if (
                    y == this.LAST &&
                    x <= this.HSIZE + 3 &&
                    x >= this.HSIZE - 3
                ) {
                    core.actions._clickSL(x, y);
                    return true;
                }
            }
            return _sys_longClick_lockControl.call(this, x, y);
        };
        core.registerAction(
            'longClick',
            '_sys_longClick_lockControl',
            core.actions._sys_longClick_lockControl,
            50
        );

        core.control._moveHero_moving = function () {
            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();
        };

        core.control._drawHero_updateViewport = function (x, y, offset) {};

        var _drawThumbnail_drawTempCanvas =
            core.maps._drawThumbnail_drawTempCanvas;
        core.maps._drawThumbnail_drawTempCanvas = function (
            floorId,
            blocks,
            options
        ) {
            if (main.mode == 'editor') {
                _drawThumbnail_drawTempCanvas.call(
                    this,
                    floorId,
                    blocks,
                    options
                );
                return;
            }

            var width = core.floors[floorId].width;
            var height = core.floors[floorId].height;
            // 绘制到tempCanvas上面
            var tempCanvas = core.bigmap.tempCanvas;
            options.v2 = false;
            tempCanvas.canvas.width = width * 32;
            tempCanvas.canvas.height = height * 32;
            options.ctx = tempCanvas;

            var hasHero = core.status.hero != null,
                flags = null;
            if (options.flags) {
                if (!hasHero) core.status.hero = {};
                flags = core.status.hero.flags;
                core.status.hero.flags = options.flags;
            }
            this._drawThumbnail_realDrawTempCanvas(floorId, blocks, options);

            if (!hasHero) delete core.status.hero;
            else if (flags != null) core.status.hero.flags = flags;
            tempCanvas.setTransform(1, 0, 0, 1, 0, 0);
        };

        var _drawThumbnail_drawToTarget = core.maps._drawThumbnail_drawToTarget;
        core.maps._drawThumbnail_drawToTarget = function (floorId, options) {
            if (main.mode == 'editor') {
                _drawThumbnail_drawToTarget.call(this, floorId, options);
                return;
            }

            var ctx = core.getContextByName(options.ctx);
            if (ctx == null) return;
            var x = options.x || 0,
                y = options.y || 0,
                size = options.size || core.__PIXELS__;
            var tempCanvas = core.bigmap.tempCanvas;
            core.drawImage(
                ctx,
                tempCanvas.canvas,
                32,
                32,
                core.__PIXELS__,
                core.__PIXELS__,
                x,
                y,
                size,
                size
            );
        };

        var _drawWindowSkin = core.ui.drawWindowSkin;
        core.ui.drawWindowSkin = function (
            background,
            ctx,
            x,
            y,
            w,
            h,
            direction,
            px,
            py
        ) {
            _drawWindowSkin.call(
                this,
                background,
                ctx,
                x,
                y,
                w,
                h,
                direction,
                px,
                py
            );

            var c = parseInt(w / 2);
            core.drawImage(
                ctx,
                background,
                160,
                90,
                16,
                6,
                x + c - 8,
                y,
                16,
                6
            );
        };

        var _drawPagination = ui.prototype.drawPagination;
        core.ui.drawPagination = function (page, totalPage, y) {
            if (
                [
                    'save',
                    'load',
                    'replayLoad',
                    'replayRemain',
                    'replaySince'
                ].indexOf(core.status.event.id) >= 0
            ) {
                if (totalPage <= 1) return;
                if (y == null) y = this.LAST;

                core.setFillStyle('ui', '#DDDDDD');
                var length = core.calWidth(
                    'ui',
                    page,
                    this._buildFont(15, true)
                );

                core.setTextAlign('ui', 'left');
                core.fillText(
                    'ui',
                    page,
                    parseInt((this.PIXEL - length) / 2),
                    y * 32 + 19
                );

                core.setTextAlign('ui', 'center');
                if (page > 1)
                    core.fillText(
                        'ui',
                        '上一页',
                        this.HPIXEL - 48,
                        y * 32 + 19
                    );
                if (page < totalPage)
                    core.fillText(
                        'ui',
                        '下一页',
                        this.HPIXEL + 48,
                        y * 32 + 19
                    );
                return;
            }
            _drawPagination.call(this, page, totalPage, y);
        };

        core.ui._drawCenterFly = function () {
            core.lockControl();
            core.status.event.id = 'centerFly';
            var fillstyle = 'rgba(255,0,0,0.5)';
            if (core.canUseItem('centerFly')) fillstyle = 'rgba(0,255,0,0.5)';
            var toX = core.bigmap.width - 1 - core.getHeroLoc('x'),
                toY = core.bigmap.height - 1 - core.getHeroLoc('y');
            this.clearUI();
            core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#000000');
            core.drawThumbnail(null, null, {
                heroLoc: core.status.hero.loc,
                heroIcon: core.status.hero.image,
                ctx: 'ui',
                centerX: toX,
                centerY: toY
            });
            var offsetX = 1,
                offsetY = 1;
            core.fillRect(
                'ui',
                (toX - offsetX) * 32,
                (toY - offsetY) * 32,
                32,
                32,
                fillstyle
            );
            core.status.event.data = {
                x: toX,
                y: toY,
                posX: toX - offsetX,
                posY: toY - offsetY
            };
            core.playSound('打开界面');
            core.drawTip(
                '请确认当前' + core.material.items['centerFly'].name + '的位置',
                'centerFly'
            );
            return;
        };

        core.ui.drawHelp = function () {
            core.clearUI();
            core.status.event.id = 'help';
            core.lockControl();
            core.setAlpha('ui', 1);
            core.fillRect('ui', 0, 0, this.PIXEL, this.PIXEL, '#FFFFFF');
            core.drawImage(
                'ui',
                core.material.images.keyboard,
                0,
                0,
                416,
                416,
                0,
                0,
                352,
                352
            );
        };

        core.actions._getClickLoc = function (x, y) {
            var size = 32 * core.domStyle.scale;
            var left =
                main.dom.gameDraw.offsetLeft + main.dom.gameGroup.offsetLeft;
            var top =
                main.dom.gameDraw.offsetTop + main.dom.gameGroup.offsetTop;
            var loc = {
                x: Math.max(x - left, 0),
                y: Math.max(y - top, 0),
                size: size
            };
            return loc;
        };

        core.enemys.getDamageString = function (enemy, x, y, floorId) {
            if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
            var damage = this.getDamage(enemy, x, y, floorId);

            var color = '#000000';

            if (damage == null) {
                damage = '???';
                color = '#FF2222';
            } else {
                if (core.hasFlag('addhp')) {
                    if (damage < core.status.hero.hp) color = '#11FF11';
                    else color = '#FF2222';
                } else if (damage <= 0) color = '#11FF11';
                else if (damage < core.status.hero.hp / 3) color = '#FFFFFF';
                else if (damage < (core.status.hero.hp * 2) / 3)
                    color = '#FFFF00';
                else if (damage < core.status.hero.hp) color = '#FF9933';
                else color = '#FF2222';

                damage = core.formatBigNumber(damage, true);
                if (core.enemys.hasSpecial(enemy, 19)) damage += '+';
                if (core.enemys.hasSpecial(enemy, 21)) damage += '-';
                if (core.enemys.hasSpecial(enemy, 11)) damage += '^';
            }

            return {
                damage: damage,
                color: color
            };
        };

        core.ui._drawBook_drawDamage = function (
            index,
            enemy,
            offset,
            position
        ) {
            core.setTextAlign('ui', 'center');
            var damage = enemy.damage,
                color = '#FFFF00';
            if (damage == null) {
                damage = '不可攻击';
                color = '#FF2222';
            }
            if (damage == 0) {
                damage = '无危险';
                color = '#11FF11';
            } else {
                if (damage >= core.status.hero.hp) color = '#FF2222';
                else if (damage >= (core.status.hero.hp * 2) / 3)
                    color = '#FF9933';
                else if (damage <= 0) color = '#11FF11';
                damage = core.formatBigNumber(damage);
                if (core.enemys.hasSpecial(enemy, 19)) damage += '+';
                if (core.enemys.hasSpecial(enemy, 21)) damage += '-';
                if (core.enemys.hasSpecial(enemy, 11)) damage += '^';
            }
            if (enemy.notBomb) damage += '[b]';
            core.fillText(
                'ui',
                damage,
                offset,
                position,
                color,
                this._buildFont(13, true)
            );
        };

        core.control.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;
                core.drawTip(
                    '你没有神圣盾不能防御，受到 ' + damage + ' 点魔法伤害'
                );
                core.playSound('zone.mp3');
                this._checkBlock_disableQuickShop();
                core.status.hero.statistics.extraDamage += damage;
                if (core.status.hero.hp <= 0) {
                    core.status.hero.hp = 0;
                    core.updateStatusBar();
                    core.events.lose();
                    return;
                } else {
                    core.updateStatusBar();
                }
            }
            this._checkBlock_ambush(core.status.checkBlock.ambush[loc]);
            this._checkBlock_repulse(core.status.checkBlock.repulse[loc]);
        };

        core.registerSystemEvent('man', function (data, callback) {
            var a = parseInt(core.status.floorId.substring(2));
            var b = core.nextX();
            var c = core.nextY();
            core.insertAction([
                { type: 'insert', name: '对话', args: [a, b, c, 0] }
            ]);
            //console.log(data);
            if (callback) callback();
        });

        core.registerSystemEvent('woman', function (data, callback) {
            var name =
                core.status.floorId +
                '@' +
                core.nextX() +
                '@' +
                core.nextY() +
                '@' +
                'A';
            if (core.getFlag(name, 0) == 1) {
                var a = parseInt(core.status.floorId.substring(2));
                var b = core.nextX();
                var c = core.nextY();
                core.insertAction([
                    { type: 'insert', name: '对话', args: [a, b, c, 1] }
                ]);
            } else {
                core.insertAction([
                    { type: 'insert', name: '商人', args: [0] }
                ]);
            }
            //console.log(data);
            if (callback) callback();
        });

        core.registerSystemEvent('fakeWall', function (data, callback) {
            if (data.event.id == 'blueWall') {
                core.insertAction([
                    { type: 'openDoor', loc: [data.x, data.y] }
                ]);
            }
            //console.log(data);
            if (callback) callback();
        });

        core.ui._drawWindowSelector = function (background, x, y, w, h) {
            w = Math.round(w) + 48;
            h = Math.round(h);
            var ctx = core.ui.createCanvas('_selector', x - 24, y, w, h, 165);
            ctx.canvas.id = '';
            this._drawSelector(ctx, background, w, h);
        };

        core.ui._drawSelector = function (ctx, background, w, h, left, top) {
            left = left || 0;
            top = top || 0;
            ctx = this.getContextByName(ctx);
            if (!ctx) return;
            if (typeof background == 'string')
                background = core.material.images.images[background];
            if (!(background instanceof Image)) return;
            // badge
            ctx.drawImage(
                background,
                132,
                68,
                24,
                24,
                left + 4,
                top + 4,
                24,
                24
            );
            ctx.drawImage(
                background,
                132,
                68,
                24,
                24,
                w - left - 28,
                top + 4,
                24,
                24
            );
        };

        core.ui.drawTip = function (text, id, clear) {
            core.ui.statusBar.print(text);
        };

        core.ui.clearMap = function (name, x, y, width, height) {
            if (name == 'all') {
                for (var m in core.canvas) {
                    core.canvas[m].clearRect(
                        0,
                        0,
                        core.bigmap.width * 32,
                        core.bigmap.height * 32
                    );
                }
                core.clearMap('outerUI');
                core.dom.gif.innerHTML = '';
                core.removeGlobalAnimate();
            } else {
                var ctx = this.getContextByName(name);
                if (ctx)
                    ctx.clearRect(
                        x || 0,
                        y || 0,
                        width || ctx.canvas.width,
                        height || ctx.canvas.height
                    );
            }
        };

        core.control._updateStatusBar_setToolboxIcon = function () {
            core.ui.statusBar._update_toolBox();
        };

        var _changeFloor_getInfo = core.events._changeFloor_getInfo;
        core.events._changeFloor_getInfo = function (
            floorId,
            stair,
            heroLoc,
            time
        ) {
            var info = _changeFloor_getInfo.call(
                this,
                floorId,
                stair,
                heroLoc,
                time
            );
            if (info == null) return null;
            info.time = 0;
            info.origin = floorId;
            return info;
        };

        core.events._changeFloor_afterChange = function (info, callback) {
            if (!info.locked) core.unlockControl();
            core.status.replay.animate = false;
            core.events.afterChangeFloor(info.floorId);

            if (info.origin == ':before') core.ui.statusBar.print('走下了楼梯');
            else if (info.origin == ':next')
                core.ui.statusBar.print('登上了楼梯');
            if (callback) callback();
        };

        if (window.jsinterface && window.jsinterface.requestLandscape) {
            window.jsinterface.requestLandscape();
        }
    },
    shop: function () {
        // 【全局商店】相关的功能
        //
        // 打开一个全局商店
        // shopId：要打开的商店id；noRoute：是否不计入录像
        this.openShop = function (shopId, noRoute) {
            var shop = core.status.shops[shopId];
            // Step 1: 检查能否打开此商店
            if (!this.canOpenShop(shopId)) {
                core.drawTip('该商店尚未开启');
                return false;
            }

            // Step 2: （如有必要）记录打开商店的脚本事件
            if (!noRoute) {
                core.status.route.push('shop:' + shopId);
            }

            // Step 3: 检查道具商店 or 公共事件
            if (shop.item) {
                if (core.openItemShop) {
                    core.openItemShop(shopId);
                } else {
                    core.playSound('操作失败');
                    core.insertAction(
                        '道具商店插件不存在！请检查是否存在该插件！'
                    );
                }
                return;
            }
            if (shop.commonEvent) {
                core.insertCommonEvent(shop.commonEvent, shop.args);
                return;
            }

            _shouldProcessKeyUp = true;

            // Step 4: 执行标准公共商店
            core.insertAction(this._convertShop(shop));
            return true;
        };

        ////// 将一个全局商店转变成可预览的公共事件 //////
        this._convertShop = function (shop) {
            return [
                {
                    type: 'function',
                    function: "function() {core.addFlag('@temp@shop', 1);}"
                },
                {
                    type: 'while',
                    condition: 'true',
                    data: [
                        // 检测能否访问该商店
                        {
                            type: 'if',
                            condition: "core.isShopVisited('" + shop.id + "')",
                            true: [
                                // 可以访问，直接插入执行效果
                                {
                                    type: 'function',
                                    function:
                                        "function() { core.plugin._convertShop_replaceChoices('" +
                                        shop.id +
                                        "', false) }"
                                }
                            ],
                            false: [
                                // 不能访问的情况下：检测能否预览
                                {
                                    type: 'if',
                                    condition: shop.disablePreview,
                                    true: [
                                        // 不可预览，提示并退出
                                        { type: 'playSound', name: '操作失败' },
                                        '当前无法访问该商店！',
                                        { type: 'break' }
                                    ],
                                    false: [
                                        // 可以预览：将商店全部内容进行替换
                                        {
                                            type: 'tip',
                                            text: '当前处于预览模式，不可购买'
                                        },
                                        {
                                            type: 'function',
                                            function:
                                                "function() { core.plugin._convertShop_replaceChoices('" +
                                                shop.id +
                                                "', true) }"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                },
                {
                    type: 'function',
                    function: "function() {core.addFlag('@temp@shop', -1);}"
                }
            ];
        };

        this._convertShop_replaceChoices = function (shopId, previewMode) {
            var shop = core.status.shops[shopId];
            var choices = (shop.choices || [])
                .filter(function (choice) {
                    if (choice.condition == null || choice.condition == '')
                        return true;
                    try {
                        return core.calValue(choice.condition);
                    } catch (e) {
                        return true;
                    }
                })
                .map(function (choice) {
                    var ableToBuy = core.calValue(choice.need);
                    return {
                        text: choice.text,
                        icon: choice.icon,
                        color:
                            ableToBuy && !previewMode
                                ? choice.color
                                : [153, 153, 153, 1],
                        action:
                            ableToBuy && !previewMode
                                ? [{ type: 'playSound', name: '商店' }].concat(
                                      choice.action
                                  )
                                : [
                                      { type: 'playSound', name: '操作失败' },
                                      {
                                          type: 'tip',
                                          text: previewMode
                                              ? '预览模式下不可购买'
                                              : '购买条件不足'
                                      }
                                  ]
                    };
                })
                .concat({
                    text: '离开',
                    action: [
                        { type: 'playSound', name: '取消' },
                        { type: 'break' }
                    ]
                });
            core.insertAction({
                type: 'choices',
                text: shop.text,
                choices: choices
            });
        };

        /// 是否访问过某个快捷商店
        this.isShopVisited = function (id) {
            if (!core.hasFlag('__shops__')) core.setFlag('__shops__', {});
            var shops = core.getFlag('__shops__');
            if (!shops[id]) shops[id] = {};
            return shops[id].visited;
        };

        /// 当前应当显示的快捷商店列表
        this.listShopIds = function () {
            return Object.keys(core.status.shops).filter(function (id) {
                return (
                    core.isShopVisited(id) || !core.status.shops[id].mustEnable
                );
            });
        };

        /// 是否能够打开某个商店
        this.canOpenShop = function (id) {
            if (this.isShopVisited(id)) return true;
            var shop = core.status.shops[id];
            if (shop.item || shop.commonEvent || shop.mustEnable) return false;
            return true;
        };

        /// 启用或禁用某个快捷商店
        this.setShopVisited = function (id, visited) {
            if (!core.hasFlag('__shops__')) core.setFlag('__shops__', {});
            var shops = core.getFlag('__shops__');
            if (!shops[id]) shops[id] = {};
            if (visited) shops[id].visited = true;
            else delete shops[id].visited;
        };

        /// 能否使用快捷商店
        this.canUseQuickShop = function (id) {
            // 如果返回一个字符串，表示不能，字符串为不能使用的提示
            // 返回null代表可以使用

            // 检查当前楼层的canUseQuickShop选项是否为false
            if (core.status.thisMap.canUseQuickShop === false)
                return '当前楼层不能使用快捷商店。';
            return null;
        };

        var _shouldProcessKeyUp = true;

        /// 允许商店X键退出
        core.registerAction(
            'keyUp',
            'shops',
            function (keycode) {
                if (
                    !core.status.lockControl ||
                    core.status.event.id != 'action'
                )
                    return false;
                if ((keycode == 13 || keycode == 32) && !_shouldProcessKeyUp) {
                    _shouldProcessKeyUp = true;
                    return true;
                }

                if (
                    !core.hasFlag('@temp@shop') ||
                    core.status.event.data.type != 'choices'
                )
                    return false;
                var data = core.status.event.data.current;
                var choices = data.choices;
                var topIndex = core.actions._getChoicesTopIndex(choices.length);
                if (keycode == 88 || keycode == 27) {
                    // X, ESC
                    core.actions._clickAction(
                        core.actions.HSIZE,
                        topIndex + choices.length - 1
                    );
                    return true;
                }
                return false;
            },
            60
        );

        /// 允许长按空格或回车连续执行操作
        core.registerAction(
            'keyDown',
            'shops',
            function (keycode) {
                if (
                    !core.status.lockControl ||
                    !core.hasFlag('@temp@shop') ||
                    core.status.event.id != 'action'
                )
                    return false;
                if (core.status.event.data.type != 'choices') return false;
                var data = core.status.event.data.current;
                var choices = data.choices;
                var topIndex = core.actions._getChoicesTopIndex(choices.length);
                if (keycode == 13 || keycode == 32) {
                    // Space, Enter
                    core.actions._clickAction(
                        core.actions.HSIZE,
                        topIndex + core.status.event.selection
                    );
                    _shouldProcessKeyUp = false;
                    return true;
                }
                return false;
            },
            60
        );

        // 允许长按屏幕连续执行操作
        core.registerAction(
            'longClick',
            'shops',
            function (x, y, px, py) {
                if (
                    !core.status.lockControl ||
                    !core.hasFlag('@temp@shop') ||
                    core.status.event.id != 'action'
                )
                    return false;
                if (core.status.event.data.type != 'choices') return false;
                var data = core.status.event.data.current;
                var choices = data.choices;
                var topIndex = core.actions._getChoicesTopIndex(choices.length);
                if (
                    x >= core.actions.CHOICES_LEFT &&
                    x <= core.actions.CHOICES_RIGHT &&
                    y >= topIndex &&
                    y < topIndex + choices.length
                ) {
                    core.actions._clickAction(x, y);
                    return true;
                }
                return false;
            },
            60
        );
    },
    magic: function () {
        function findProperty(obj, key) {
            const keys = key.split('.');
            let now = obj;
            while (keys.length > 1) {
                const key = keys.pop();
                now = now[key];
            }
            return [now, keys[0]];
        }

        // replay 1
        core.registerReplayAction('setItem', action => {
            if (!action.startsWith('setItem:')) return false;
            const [, id, value] = action.split(':');
            const num = Number(value);
            if (isNaN(num)) return false;
            if (num < 0) return false;
            core.setItem(id, num);
            core.status.route.push(action);
            core.replay();
            return true;
        });

        // replay 2
        core.registerReplayAction('toggle', action => {
            if (!action.startsWith('toggle:')) return false;
            const [, id] = action.split(':');
            const base = core.status.hero.flags;
            const key = id;
            // Revert boolean.
            base[key] = !base[key];
            core.status.route.push(action);
            core.replay();
            return true;
        });

        // replay 3
        core.registerReplayAction('MT30', action => {
            if (!action.startsWith('MT30:')) return false;
            const step = action.slice(5);
            const success = core.MT30.step(step);
            if (success) core.replay();
            return success;
        });

        function generateInstructionCount(n) {
            let a = 1,
                b = 1;
            for (let i = 0; i < n; i++) {
                [a, b] = [b, a + b * 10];
            }
            return b;
        }

        // Define a threshold to ensure that attribute adjusting is safe.
        const SAFETY_THRESHOLD = 2000;

        function adjustPlayerAttributes() {
            // Use a function to boom the result when remaining big bat count is large.
            // So that player must control remaining count to minimize the decline.
            const toMap = [
                'MT10',
                'MT11',
                'MT12',
                'MT13',
                'MT14',
                'MT15',
                'MT16',
                'MT17',
                'MT18',
                'MT19'
            ];
            const count = toMap.reduce((prev, curr) => {
                const id = core.getBlockId(6, 8, curr);
                if (id === 'bigBat') return prev + 1;
                else return prev;
            }, 0);
            const n = Math.floor(
                generateInstructionCount(count) / SAFETY_THRESHOLD
            );
            // Make a chain to adjust hero attribute.
            let effectChain = 'core.enhance.reset()';
            for (let i = 0; i < n; i++) {
                effectChain += `.atk(${0.99 * Math.sqrt(i / n)}).def(${
                    0.99 * Math.sqrt(i / n)
                })`;
            }
            effectChain += '.apply();';
            // Convert to a function.
            const func = new Function(effectChain);
            func();
        }

        const handler = {
            apply(target, thisArg, argArray) {
                // We need to add a flag...
                core.magic.attributeAdjusted = false;
                const result = target.apply(thisArg, argArray);
                core.insertAction(
                    [
                        {
                            type: 'function',
                            function: `function() {
                            for (let i = 0; i < 10; i++) {
                                core.magic.importantFlag++;
                            }
                        }`
                        }
                    ],
                    void 0,
                    void 0,
                    () => {
                        core.magic.attributeAdjusted = true;
                    }
                );
                return result;
            }
        };

        const proxy = new Proxy(adjustPlayerAttributes, handler);

        function strangeInput() {
            // Check number length, max to 3.
            if (!flags.input) return false;
            if (flags.input.length > 3) return false;
            // Only need to compare the integer part.
            const num = parseInt(flags.input);
            if (num < 1 || num > 2) return false;
            // But we need to parse it into float to ensure its correctness.
            // Limit the max value to ban exponential numbers.
            // Numbers less than 1 cannot be constructed, so we don't need to limit it.
            const hp =
                core.status.hero.hp ** Math.min(parseFloat(flags.input), 2);
            if (!isNaN(hp)) {
                core.status.hero.hp **= Math.min(parseFloat(flags.input), 2);
                return true;
            } else {
                return false;
            }
        }

        core.magic = {
            attributeAdjusted: true,
            adjustPlayerAttributes: proxy,
            importantFlag: void 0,
            strangeCondition: true,
            findProperty,
            strangeInput
        };
    },
    magicOverride: function () {
        const originGetDamageInfo = core.enemys.getDamageInfo;
        core.enemys.getDamageInfo = function (...params) {
            // For debugging, temporary disable item count gain.
            const before = core.status.hero.items;
            if (flags.ignoreItemCount) {
                core.status.hero.items = {
                    tools: {},
                    equips: {},
                    constants: {}
                };
            }
            const info = originGetDamageInfo.apply(core.enemys, params);
            core.status.hero.items = before;
            return info;
        };

        utils.prototype.formatBigNumber = function (x, digits) {
            if (digits === true) digits = 5; // 兼容旧版onMap参数
            if (!digits || digits < 5) digits = 6; // 连同负号、小数点和后缀字母在内的总位数，至少需为5，默认为6
            x = Math.trunc(parseFloat(x)); // 尝试识别为小数，然后向0取整
            if (x == null) return '???'; // 无法识别的数，显示'???'
            // change 4: Show Inf when number is not finite.
            if (!isFinite(x)) return x < 0 ? '-Inf' : 'Inf';
            var units = [
                // 单位及其后缀字母，可自定义，如改成千进制下的K、M、G、T、P
                { val: 1e4, suffix: 'w' },
                { val: 1e8, suffix: 'e' },
                { val: 1e12, suffix: 'z' },
                { val: 1e16, suffix: 'j' },
                { val: 1e20, suffix: 'g' }
            ];
            if (Math.abs(x) > 1e20 * Math.pow(10, digits - 2))
                return x.toExponential(0); // 绝对值过大以致于失去精度的数，直接使用科学记数法，系数只保留整数
            var sign = x < 0 ? '-' : '';
            if (sign) --digits; // 符号位单独处理，负号要占一位
            x = Math.abs(x);

            if (x < Math.pow(10, digits)) return sign + x;

            for (var i = 0; i < units.length; ++i) {
                var each = units[i];
                var u = (x / each.val).toFixed(digits).substring(0, digits);
                if (u.indexOf('.') < 0) continue;
                u = u.substring(
                    0,
                    u[u.length - 2] == '.' ? u.length - 2 : u.length - 1
                );
                return sign + u + each.suffix;
            }
            return sign + x.toExponential(0);
        };

        // change 10: Add a hook
        core.control.hooks ??= [];
        control.prototype.unlockControl = function () {
            core.status.lockControl = false;
            this.hooks.forEach(v => v());
            this.hooks = [];
        };

        // change 12: Modify can battle condition.
        enemys.prototype.canBattle = function (enemy, x, y, floorId) {
            if (typeof enemy == 'string') enemy = core.material.enemys[enemy];
            var damage = this.getDamage(enemy, x, y, floorId);
            // First, we should judge whether damage is null.
            if (damage === null || damage === void 0) return false;
            // Then if damage less than zero, return true.
            if (damage <= 0) return true;
            // Because of the particularity of this game, we should calculate the final hp hurt by enemy.
            const hp = core.status.hero.hp - damage;
            // Then judge whether hp is not less than zero.
            return hp >= 0;
        };

        // change 13: Modify clone deep action.
        utils.prototype.clone = function (data, filter, recursion) {
            if (data === null || data === void 0) return data;
            // date
            if (data instanceof Date) {
                var copy = new Date();
                copy.setTime(data.getTime());
                return copy;
            }
            // array
            if (data instanceof Array) {
                var copy = [];
                for (var i in data) {
                    if (!filter || filter(i, data[i]))
                        copy[i] = core.clone(
                            data[i],
                            recursion ? filter : null,
                            recursion
                        );
                }
                return copy;
            }
            // 函数
            if (data instanceof Function) {
                return data;
            }
            // object
            if (data instanceof Object) {
                var copy = {};
                for (var i in data) {
                    if (
                        data.hasOwnProperty(i) &&
                        (!filter || filter(i, data[i]))
                    )
                        copy[i] = core.clone(
                            data[i],
                            recursion ? filter : null,
                            recursion
                        );
                }
                return copy;
            }
            return data;
        };

        // Modify move replay action
        core.control._replayAction_move = function (action) {
            if (['up', 'down', 'left', 'right'].indexOf(action) < 0)
                return false;
            if (core.status.floorId === 'MT30' && !core.MT30.success())
                return false;
            core.moveHero(action, core.replay);
            return true;
        };
        core.registerReplayAction('move', core.control._replayAction_move);
    },
    pointedEnemy: function () {
        // Rewrite pointed enemy, since template's implementation is hard to use.
        core.pointed = new (class {
            /** @type {Map<string, Map<number, Record<string, any>>>} */
            #store = new Map();

            reset() {
                this.#store.clear();
            }

            #ensureStore(floorId) {
                if (this.#store.has(floorId)) {
                    return this.#store.get(floorId);
                } else {
                    const map = new Map();
                    this.#store.set(floorId, map);
                    return map;
                }
            }

            #ensureLoc(map, index) {
                if (map.has(index)) {
                    return map.get(index);
                } else {
                    const obj = {};
                    map.set(index, obj);
                    return obj;
                }
            }

            getAll(x, y, floor) {
                const map = this.#ensureStore(floor);
                const { width } = core.floors[floor];
                const index = x + width * y;
                const obj = this.#ensureLoc(map, index);
                return obj;
            }

            setAll(x, y, floor, value) {
                const map = this.#ensureStore(floor);
                const { width } = core.floors[floor];
                const index = x + width * y;
                map.set(index, value);
            }

            set(x, y, floor, property, value) {
                const obj = this.getAll(x, y, floor);
                obj[property] = value;
            }

            get(x, y, floor, property) {
                const obj = this.getAll(x, y, floor);
                return obj[property];
            }

            move(fx, fy, ff, tx, ty, tf) {
                const from = this.getAll(fx, fy, ff);
                this.setAll(tx, ty, tf, from);
            }

            with(enemy, x, y, floor, property) {
                const value = this.get(x, y, floor, property);
                // Ignore when value is not defined or null.
                if (value !== void 0 && value !== null) return value;
                return enemy[property];
            }

            stringify() {
                const obj = {};
                // Convert to object.
                this.#store.forEach((v, k) => {
                    obj[k] = Object.fromEntries([...v.entries()]);
                });
                return JSON.stringify(obj);
            }

            parse(str) {
                const obj = JSON.parse(str);
                // Reset first.
                this.reset();
                for (const [key, value] of Object.entries(obj)) {
                    const map = new Map();
                    for (const [k, v] of Object.entries(value)) {
                        // Ignore null keys.
                        if (v !== null) map.set(k, v);
                    }
                    this.#store.set(key, map);
                }
            }

            optimizeMemory() {
                // Use stringify and parse to free some momery.
                this.parse(this.stringify());
            }
        })();
    },
    enhance: function () {
        core.enhance = new (class {
            #atk = 0;
            #def = 0;

            reset() {
                this.#atk = 0;
                this.#def = 0;
                return this;
            }

            atk(num) {
                this.#atk *= num;
                return this;
            }

            def(num) {
                this.#def *= num;
                return this;
            }

            apply() {
                core.status.hero.atk *= this.#atk;
                core.status.hero.def *= this.#def;
                this.reset();
            }
        })();
    },
    MT40: function () {
        const dir1 = {
            up: 'right',
            right: 'left',
            down: 'down',
            left: 'up'
        };
        const dir2 = {
            up: 'down',
            right: 'right',
            down: 'left',
            left: 'up'
        };

        core.MT40 = new (class {
            boss1 = [8, 6];
            boss2 = [2, 1];

            reset() {
                this.boss1 = [8, 6];
                this.boss2 = [2, 1];
            }

            #inBorder([x, y]) {
                return x === 0 || x === 12 || y === 0 || y === 12;
            }

            #inside([x, y]) {
                return x <= 11 && x >= 1 && y <= 11 && y >= 1;
            }

            step(dir) {
                if (core.status.lockControl) return;
                const boss1Dir = dir1[dir];
                const boss2Dir = dir2[dir];

                const { x: dx1, y: dy1 } = core.utils.scan[boss1Dir];
                const { x: dx2, y: dy2 } = core.utils.scan[boss2Dir];

                const newBoss1 = [this.boss1[0] + dx1, this.boss1[1] + dy1];
                const newBoss2 = [this.boss2[0] + dx2, this.boss2[1] + dy2];

                const actions = [];
                if (this.#inside(newBoss1)) {
                    actions.push({
                        type: 'move',
                        time: 100,
                        keep: true,
                        async: true,
                        loc: this.boss1,
                        steps: [`${boss1Dir}:1`]
                    });
                }
                if (this.#inside(newBoss2)) {
                    actions.push({
                        type: 'move',
                        time: 100,
                        keep: true,
                        async: true,
                        loc: this.boss2,
                        steps: [`${boss2Dir}:1`]
                    });
                }

                actions.push({ type: 'waitAsync' });

                core.insertAction(actions, void 0, void 0, () => {
                    if (this.#inside(newBoss1)) this.boss1 = newBoss1;
                    if (this.#inside(newBoss2)) this.boss2 = newBoss2;
                    if (this.#inBorder(this.boss1)) {
                        core.setBlock(
                            'yellowKnight',
                            this.boss1[0],
                            this.boss1[1]
                        );
                    }
                    if (this.#inBorder(this.boss2)) {
                        core.setBlock(
                            'redKnight',
                            this.boss2[0],
                            this.boss2[1]
                        );
                    }
                    // Decline the score.
                    core.score.minus(50);
                });
            }

            stringify() {
                return JSON.stringify([this.boss1, this.boss2]);
            }

            parse(str) {
                this.reset();
                const arr = JSON.parse(str);
                if (arr.length === 2) {
                    this.boss1 = arr[0];
                    this.boss2 = arr[1];
                }
            }
        })();
    },
    MT30: function () {
        const MAX_STEP = 10;

        core.MT30 = new (class {
            #nowStep = 0;
            #success = false;
            #nowLoc = [1, 1];
            #targetLoc = [11, 11];

            reset() {
                this.#nowStep = 0;
                this.#nowLoc = [1, 1];
                this.#targetLoc = [11, 11];
                this.#success = false;
                const toDelete = [];
                core.extractBlocks('MT30');
                core.status.maps.MT30.blocks.forEach(v => {
                    if (v.event.id === 'box') toDelete.push([v.x, v.y]);
                });
                toDelete.forEach(v => {
                    core.removeBlock(v[0], v[1], 'MT30');
                });
                core.setBlock('box', 1, 1, 'MT30');
            }

            step(action) {
                const [dir, value] = action.split(':');
                const num = Number(value);
                if (isNaN(num)) {
                    this.reset();
                    return false;
                }
                if (this.#nowStep + num > MAX_STEP) {
                    this.reset();
                    console.warn(`超出步数上限！`);
                    return false;
                }
                this.#nowStep += num;
                const { x: dx, y: dy } = core.utils.scan[dir];
                const next = [
                    this.#nowLoc[0] + dx * num,
                    this.#nowLoc[1] + dy * num
                ];
                const id = core.getBlockId(next[0], next[1], 'MT30');
                if (id !== null && id !== void 0 && id !== 'flower') {
                    return false;
                }
                core.removeBlock(this.#nowLoc[0], this.#nowLoc[1], 'MT30');
                this.#nowLoc = [
                    core.clamp(next[0], 1, 11),
                    core.clamp(next[1], 1, 11)
                ];
                core.setBlock('box', this.#nowLoc[0], this.#nowLoc[1], 'MT30');
                if (
                    this.#nowLoc[0] === this.#targetLoc[0] &&
                    this.#nowLoc[1] === this.#targetLoc[1]
                ) {
                    this.#success = true;
                    if (id !== 'flower') {
                        core.score.add(750);
                    }
                    core.insertAction(
                        [{ type: 'openDoor', loc: [6, 2] }],
                        void 0,
                        void 0,
                        () => {
                            core.unlockControl();
                        }
                    );
                }
                core.status.route.push(`MT30:${action}`);
                return true;
            }

            enter() {
                if (!this.#success) {
                    core.lockControl();
                    core.registerAction(
                        'keyUp',
                        'MT30',
                        code => {
                            if (this.#success) return false;
                            if (code === 37) {
                                this.step('left:1');
                            } else if (code === 38) {
                                this.step('up:1');
                            } else if (code === 39) {
                                this.step('right:1');
                            } else if (code === 40) {
                                this.step('down:1');
                            }
                            return true;
                        },
                        150
                    );
                }
            }

            leave() {
                core.unlockControl();
                core.unregisterAction('keyUp', 'MT30');
            }

            stringify() {
                return JSON.stringify({
                    success: this.#success,
                    nowLoc: this.#nowLoc,
                    nowStep: this.#nowStep
                });
            }

            parse(str) {
                this.reset();
                const obj = JSON.parse(str);
                this.#success = obj.success;
                this.#nowLoc = obj.nowLoc;
                this.#nowStep = obj.nowStep;
                core.setBlock('box', this.#nowLoc[0], this.#nowLoc[1], 'MT30');
            }

            success() {
                return this.#success;
            }
        })();
    },
    score: function () {
        core.score = new (class {
            #score = 0;

            reset() {
                this.#score = 0;
            }

            add(point) {
                this.#score += point;
            }

            minus(point) {
                this.#score -= point;
            }

            sum() {
                // Sum the score, with intrinsic scores.
                let score = 0;
                // First to add scores added.
                score += this.#score;
                // Second to add 1e4.
                score += 100_00;
                // Third to sum key route actions.
                core.status.route.forEach(v => {
                    // setItem to be weight 50.
                    if (v.startsWith('setItem:')) {
                        score -= 50;
                    }
                    // toggle to be weight 150.
                    if (v.startsWith('toggle:')) {
                        score -= 150;
                    }
                    // MT30 to be 400.
                    if (v.startsWith('MT30:')) {
                        score -= 400;
                    }
                });
                // Fourth to add misc scores.
                const forEachObj = (obj, fn) => {
                    if (typeof obj !== 'object' || !obj) {
                        fn(obj);
                        return;
                    }
                    for (const [, value] of Object.entries(obj)) {
                        forEachObj(value, fn);
                    }
                };
                score -= core.status.hero.statistics.battle * 25;
                if (flags.dev) {
                    return 1;
                }
                return score;
            }

            stringify() {
                return JSON.stringify(this.#score);
            }

            parse(str) {
                this.reset();
                this.#score = JSON.parse(str);
            }
        })();
    },
    utils: function () {
        /**
         * 计算方程 10k³ + 50k - 3a = 0 的实根 k, By DeepSeek R1
         * @param {number} a - 已知的总和 S(k) = a
         * @param {number} tolerance - 容差，默认 1e-20
         * @param {number} maxIterations - 最大迭代次数，默认 100
         * @returns {number} 近似解 k
         */
        function findK(a, tolerance = 1e-20, maxIterations = 100) {
            if (a <= 0) return 0; // 处理非正数输入

            // 初始猜测：k ≈ (3a/10)^(1/3)，因 k³ 主导
            let k = Math.pow((3 * a) / 10, 1 / 3);

            for (let i = 0; i < maxIterations; i++) {
                // 计算 f(k) 和 f'(k)
                const f = 10 * k ** 3 + 50 * k - 3 * a;
                const fPrime = 30 * k ** 2 + 50;

                if (!isFinite(f) || !isFinite(fPrime)) return Infinity;

                // 牛顿迭代公式：k_new = k - f(k)/f'(k)
                const delta = f / fPrime;
                k -= delta;

                // 判断是否收敛
                if (Math.abs(delta) < tolerance) {
                    return k;
                }
            }

            return k; // 返回最后一次迭代结果（可能未完全收敛）
        }

        /** $\sigma_{n=1}^{n=k}{20+10n(n-1)}=\frac{10k^3+50k}{3}$ */
        function sumK(k) {
            return Math.round((10 * k ** 3 + 50 * k) / 3);
        }

        function asyncInsertAction(action, x, y, callback, addToLast) {
            return new Promise(res =>
                core.insertAction(
                    action,
                    x,
                    y,
                    () => {
                        res();
                        callback?.();
                    },
                    addToLast
                )
            );
        }

        core.functional = {
            findK,
            sumK,
            asyncInsertAction
        };
    },
    changes: function () {
        // All changes are listed below.
        // ---
        // All codes following with comment starts with:
        //   perf, damage, replay, change
        // All new plugins in plugins.js
        // ---
        // Common event: 商店
        // pointed event changed(exclude new event):
        //   MT28, 8,4,
        //   MT10, afterBattle,
        //   MT20, Boss battle,
        //   MT30,
        //   MT40, Boss battle,
        //   MT3, 5,9
        //   MT29, 6,2
        //   MT35, adfterBattle
        //
        // Almost all man dialogues.
        // Add some new event. You can check it in editor.
        // ---
        // Delete talking cross.
        // ---
        // Modify all items' effect.
        // Modify all enemies' attributes.
    }
};
