diff --git a/index.html b/index.html
index dd3f98e..90046da 100644
--- a/index.html
+++ b/index.html
@@ -142,7 +142,7 @@
                 <canvas class='gameCanvas draw-canvas hide' id='event2'></canvas>
                 <canvas class='gameCanvas draw-canvas hide' id='fg'></canvas>
                 <canvas class='gameCanvas hide' id='damage'></canvas>
-                <canvas class='gameCanvas' id='animate'></canvas>
+                <canvas class='gameCanvas hide' id='animate'></canvas>
                 <canvas class='gameCanvas hide' id='curtain'></canvas>
                 <canvas class='gameCanvas' id='ui'></canvas>
                 <canvas class='gameCanvas' id='data'>此浏览器不支持HTML5</canvas>
diff --git a/src/core/fx/shadow.ts b/src/core/fx/shadow.ts
index 176bcf1..a072755 100644
--- a/src/core/fx/shadow.ts
+++ b/src/core/fx/shadow.ts
@@ -121,6 +121,7 @@ hook.once('reset', () => {
     Mota.rewrite(core.control, 'loadData', 'add', () => {
         if (!main.replayChecking) {
             Shadow.update(true);
+            LayerShadowExtends.shadowList.forEach(v => v.update());
         }
     });
     // Mota.require('var', 'hook').on('changingFloor', (floorId) => {
@@ -141,7 +142,7 @@ hook.on('setBlock', () => {
 })
 hook.on('changingFloor', floorId => {        
     Shadow.clearBuffer();
-    Shadow.update();
+    Shadow.update(true);
     // setCanvasFilterByFloorId(floorId);
     LayerShadowExtends.shadowList.forEach(v => v.update());
 })
diff --git a/src/core/plugin.ts b/src/core/plugin.ts
index 6da8b90..4e54ee9 100644
--- a/src/core/plugin.ts
+++ b/src/core/plugin.ts
@@ -17,8 +17,6 @@
 // import frag from '@/plugin/fx/frag';
 // import { Mota } from '.';
 
-import * as shadow from '@/plugin/shadow/shadow';
-import * as gameShadow from '@/plugin/shadow/gameShadow';
 import * as fly from '@/plugin/ui/fly';
 import * as chase from '@/plugin/chase/chase';
 import * as completion from '@/plugin/completion';
@@ -29,8 +27,6 @@ import * as gameCanvas from '@/plugin/fx/gameCanvas';
 import * as smooth from '@/plugin/fx/smoothView';
 import * as animateController from '@/plugin/animateController';
 
-Mota.Plugin.register('shadow_r', shadow, shadow.init);
-Mota.Plugin.register('gameShadow_r', gameShadow, gameShadow.init);
 Mota.Plugin.register('fly_r', fly);
 Mota.Plugin.register('chase_r', chase);
 Mota.Plugin.register('completion_r', completion, completion.init);
diff --git a/src/core/render/index.ts b/src/core/render/index.ts
index aa061f6..963a760 100644
--- a/src/core/render/index.ts
+++ b/src/core/render/index.ts
@@ -8,6 +8,7 @@ import { LayerShadowExtends } from '../fx/shadow';
 import { LayerGroupFilter } from '@/plugin/fx/gameCanvas';
 import { LayerGroupAnimate } from './preset/animate';
 import { LayerGroupPortal } from '@/plugin/fx/portal';
+import { LayerGroupHalo } from '@/plugin/fx/halo';
 
 let main: MotaRenderer;
 
@@ -35,16 +36,19 @@ Mota.require('var', 'loading').once('loaded', () => {
     const filter = new LayerGroupFilter();
     const animate = new LayerGroupAnimate();
     const portal = new LayerGroupPortal();
+    const halo = new LayerGroupHalo();
     layer.extends(damage);
     layer.extends(detail);
     layer.extends(filter);
     layer.extends(portal);
+    layer.extends(halo);
     layer.getLayer('event')?.extends(hero);
     layer.getLayer('event')?.extends(door);
     layer.getLayer('event')?.extends(shadow);
     layer.extends(animate);
 
     render.appendChild(layer);
+    // console.log(render);
 });
 
 Mota.require('var', 'hook').on('reset', () => {
diff --git a/src/core/render/preset/damage.ts b/src/core/render/preset/damage.ts
index 5a04609..77dd9c2 100644
--- a/src/core/render/preset/damage.ts
+++ b/src/core/render/preset/damage.ts
@@ -56,6 +56,7 @@ export class FloorDamageExtends
     private create() {
         if (this.sprite) return;
         const sprite = new Damage();
+        sprite.setZIndex(80);
         this.group.appendChild(sprite);
         this.sprite = sprite;
     }
diff --git a/src/core/render/preset/layer.ts b/src/core/render/preset/layer.ts
index 8908488..3faf837 100644
--- a/src/core/render/preset/layer.ts
+++ b/src/core/render/preset/layer.ts
@@ -83,6 +83,14 @@ export interface ILayerGroupRenderExtends {
 
 export type FloorLayer = 'bg' | 'bg2' | 'event' | 'fg' | 'fg2';
 
+const layerZIndex: Record<FloorLayer, number> = {
+    bg: 10,
+    bg2: 20,
+    event: 30,
+    fg: 40,
+    fg2: 50
+};
+
 export class LayerGroup extends Container implements IAnimateFrame {
     /** 地图组列表 */
     // static list: Set<LayerGroup> = new Set();
@@ -190,6 +198,7 @@ export class LayerGroup extends Container implements IAnimateFrame {
         const l = new Layer();
         l.layer = layer;
         if (l.layer) this.layers.set(l.layer, l);
+        l.setZIndex(layerZIndex[layer]);
         this.appendChild(l);
 
         for (const ex of this.extend.values()) {
diff --git a/src/plugin/fx/frag.ts b/src/plugin/fx/frag.ts
index d093d97..788f260 100644
--- a/src/plugin/fx/frag.ts
+++ b/src/plugin/fx/frag.ts
@@ -1,6 +1,8 @@
 import { Animation, linear, sleep } from 'mutate-animate';
 import { has } from '../utils';
 
+// todo: 移植到渲染树
+
 interface SplittedImage {
     canvas: HTMLCanvasElement;
     x: number;
diff --git a/src/plugin/fx/gameCanvas.ts b/src/plugin/fx/gameCanvas.ts
index d7d3505..0dcf775 100644
--- a/src/plugin/fx/gameCanvas.ts
+++ b/src/plugin/fx/gameCanvas.ts
@@ -5,10 +5,6 @@ import {
     LayerGroup
 } from '@/core/render/preset/layer';
 
-export default function init() {
-    return {};
-}
-
 const filterMap: [FloorIds[], string][] = [];
 
 function getCanvasFilterByFloorId(floorId: FloorIds = core.status.floorId) {
diff --git a/src/plugin/fx/halo.ts b/src/plugin/fx/halo.ts
new file mode 100644
index 0000000..7ecd1d6
--- /dev/null
+++ b/src/plugin/fx/halo.ts
@@ -0,0 +1,106 @@
+import { logger } from '@/core/common/logger';
+import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
+import { mainSetting } from '@/core/main/setting';
+import { LayerGroupFloorBinder } from '@/core/render/preset/floor';
+import {
+    ILayerGroupRenderExtends,
+    LayerGroup
+} from '@/core/render/preset/layer';
+import { Sprite } from '@/core/render/sprite';
+import { Transform } from '@/core/render/transform';
+
+export class LayerGroupHalo implements ILayerGroupRenderExtends {
+    id: string = 'halo';
+
+    group!: LayerGroup;
+    binder!: LayerGroupFloorBinder;
+    halo!: Halo;
+
+    awake(group: LayerGroup): void {
+        this.group = group;
+        const ex = group.getExtends('floor-binder');
+        if (ex instanceof LayerGroupFloorBinder) {
+            this.binder = ex;
+            this.halo = new Halo();
+            this.halo.setHD(true);
+            this.halo.size(group.width, group.height);
+            this.halo.setZIndex(75);
+            this.halo.binder = ex;
+            group.appendChild(this.halo);
+        } else {
+            logger.error(
+                1401,
+                `Halo extends needs 'floor-binder' extends as dependency.`
+            );
+            group.removeExtends('halo');
+        }
+    }
+
+    onDestroy(group: LayerGroup): void {
+        this.halo?.destroy();
+    }
+}
+
+const haloColor: Record<number, string[]> = {
+    21: ['cyan'],
+    25: ['purple'],
+    26: ['blue'],
+    27: ['red'],
+    29: ['#3CFF49']
+};
+
+class Halo extends Sprite {
+    /** 单元格大小 */
+    cellSize: number = 32;
+    /** 当前楼层,用于获取有哪些光环 */
+    binder!: LayerGroupFloorBinder;
+
+    constructor() {
+        super('static', false);
+
+        this.setRenderFn((canvas, transform) => {
+            this.drawHalo(canvas, transform);
+        });
+    }
+
+    drawHalo(canvas: MotaOffscreenCanvas2D, transform: Transform) {
+        if (!mainSetting.getValue('screen.halo', true)) return;
+        const floorId = this.binder.getFloor();
+        if (!floorId) return;
+        const col = core.status.maps[floorId].enemy;
+        if (!col) return;
+        const [dx, dy] = col.translation;
+        const list = col.haloList.concat(
+            Object.keys(flags[`melt_${floorId}`] ?? {}).map(v => {
+                const [x, y] = v.split(',').map(v => parseInt(v));
+                return {
+                    type: 'square',
+                    data: {
+                        x: x + dx,
+                        y: y + dy,
+                        d: 3
+                    },
+                    special: 25
+                };
+            })
+        );
+        const { ctx } = canvas;
+        const cell = this.cellSize;
+        ctx.lineWidth = 1;
+        for (const halo of list) {
+            if (halo.type === 'square') {
+                const { x, y, d } = halo.data;
+                const [color, border] = haloColor[halo.special];
+                const r = Math.floor(d / 2);
+                const left = x - r;
+                const top = y - r;
+                ctx.fillStyle = color;
+                ctx.strokeStyle = border ?? color;
+                ctx.globalAlpha = 0.1;
+                ctx.fillRect(left * cell, top * cell, d * cell, d * cell);
+                ctx.globalAlpha = 0.6;
+                ctx.strokeRect(left * cell, top * cell, d * cell, d * cell);
+            }
+        }
+    }
+}
diff --git a/src/plugin/fx/itemDetail.ts b/src/plugin/fx/itemDetail.ts
index 1d13681..8013c95 100644
--- a/src/plugin/fx/itemDetail.ts
+++ b/src/plugin/fx/itemDetail.ts
@@ -227,7 +227,7 @@ export class FloorItemDetail implements ILayerGroupRenderExtends {
             for (const [key, value] of Object.entries(diff)) {
                 if (!value) continue;
                 const color = FloorItemDetail.detailColor[key] ?? '#fff';
-                const text = value.toString();
+                const text = Math.floor(value).toString();
                 const renderable: DamageRenderable = {
                     x: x * this.sprite.cellSize + 2,
                     y: y * this.sprite.cellSize + 31 - n * 10,
diff --git a/src/plugin/fx/noise.ts b/src/plugin/fx/noise.ts
deleted file mode 100644
index a22c3f4..0000000
--- a/src/plugin/fx/noise.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-export default function init() {
-    return { createGaussNoise };
-}
-
-interface GaussNoiseConfig {
-    /** 分辨率,最小为1,最大为画布长宽的最大值,默认为画布长宽最大值的一半,过大可能会卡顿 */
-    resolution?: number;
-    /** 画布宽度 */
-    width?: number;
-    /** 画布高度 */
-    height?: number;
-    /** 噪声灰度的均值,范围 0 ~ 255 */
-    expectation: number;
-    /** 噪声灰度方差 */
-    deviation: number;
-    /** 目标画布,如果不指定会创建一个新的 */
-    canvas?: HTMLCanvasElement;
-}
-
-/**
- * 创建一个高斯噪声
- * @param config 噪声选项
- * @returns 噪声画布
- */
-export function createGaussNoise(config: GaussNoiseConfig): HTMLCanvasElement {
-    const canvas = config.canvas ?? document.createElement('canvas');
-    canvas.width = config.width ?? core._PX_;
-    canvas.height = config.height ?? core._PY_;
-    canvas.style.imageRendering = 'pixelated';
-    const ctx = canvas.getContext('2d')!;
-    ctx.imageSmoothingEnabled = false;
-
-    const max = Math.max(canvas.width, canvas.height);
-    const resolution = Math.min(max, config.resolution ?? max / 2);
-
-    const step =
-        max === canvas.width
-            ? canvas.width / resolution
-            : canvas.height / resolution;
-
-    for (let x = 0; x < canvas.width; x += step) {
-        for (let y = 0; y < canvas.height; y += step) {
-            const random =
-                Math.sqrt(Math.log(Math.random()) * -2) *
-                Math.sin(2 * Math.PI * Math.random());
-            const gray = 255 - random * config.deviation - config.expectation;
-
-            ctx.fillStyle = `rgba(${gray},${gray},${gray},${gray / 255})`;
-            ctx.fillRect(x, y, step, step);
-        }
-    }
-
-    return canvas;
-}
diff --git a/src/plugin/game/enemy/checkblock.ts b/src/plugin/game/enemy/checkblock.ts
index d3fc6b9..fc00440 100644
--- a/src/plugin/game/enemy/checkblock.ts
+++ b/src/plugin/game/enemy/checkblock.ts
@@ -1,5 +1,4 @@
 import { has, ofDir } from '@/plugin/game/utils';
-import { drawHalo } from '../fx/halo';
 
 export function init() {
     // 伤害弹出
diff --git a/src/plugin/game/fallback.ts b/src/plugin/game/fallback.ts
index 9666ecf..4f52280 100644
--- a/src/plugin/game/fallback.ts
+++ b/src/plugin/game/fallback.ts
@@ -986,11 +986,8 @@ export function init() {
                 core.setHeroLoc('y', ny);
                 core.setHeroLoc('direction', action);
             }
-            if (!main.replayChecking) {
-                setTimeout(core.replay, 100);
-            } else {
-                core.replay();
-            }
+
+            setTimeout(core.replay, 100);
 
             return true;
         } else {
diff --git a/src/plugin/game/fx/halo.ts b/src/plugin/game/fx/halo.ts
deleted file mode 100644
index 4e7b71a..0000000
--- a/src/plugin/game/fx/halo.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-const haloColor: Record<number, string[]> = {
-    21: ['cyan'],
-    25: ['purple'],
-    26: ['blue'],
-    27: ['red'],
-    29: ['#3CFF49']
-};
-
-export function drawHalo(
-    ctx: CanvasRenderingContext2D,
-    onMap: boolean,
-    floorId: FloorIds
-) {
-    if (main.replayChecking) return;
-    const setting = Mota.require('var', 'mainSetting');
-    if (!setting.getValue('screen.halo', true)) return;
-    Mota.require('fn', 'ensureFloorDamage')(floorId);
-    const col = core.status.maps[floorId].enemy;
-    const [dx, dy] = col.translation;
-    const list = col.haloList.concat(
-        Object.keys(flags[`melt_${floorId}`] ?? {}).map(v => {
-            const [x, y] = v.split(',').map(v => parseInt(v));
-            return {
-                type: 'square',
-                data: {
-                    x: x + dx,
-                    y: y + dy,
-                    d: 3
-                },
-                special: 25
-            };
-        })
-    );
-    ctx.save();
-    for (const halo of list) {
-        if (halo.type === 'square') {
-            const { x, y, d } = halo.data;
-            const [color, border] = haloColor[halo.special];
-            const r = Math.floor(d / 2);
-            let left = x - r,
-                right = x + r,
-                top = y - r,
-                bottom = y + r;
-            if (onMap && core.bigmap.v2) {
-                left -= core.bigmap.posX;
-                top -= core.bigmap.posY;
-                right -= core.bigmap.posX;
-                bottom -= core.bigmap.posY;
-                if (
-                    right < -1 ||
-                    left > core._PX_ / 32 + 1 ||
-                    top < -1 ||
-                    bottom > core._PY_ / 32 + 1
-                ) {
-                    continue;
-                }
-            }
-            ctx.fillStyle = color;
-            ctx.strokeStyle = border ?? color;
-            ctx.lineWidth = 1;
-            ctx.globalAlpha = 0.1;
-            ctx.fillRect(left * 32, top * 32, d * 32, d * 32);
-            ctx.globalAlpha = 0.6;
-            ctx.strokeRect(left * 32, top * 32, d * 32, d * 32);
-        }
-    }
-
-    ctx.restore();
-}
diff --git a/src/plugin/game/fx/heroDetail.ts b/src/plugin/game/fx/heroDetail.ts
deleted file mode 100644
index 2a854c5..0000000
--- a/src/plugin/game/fx/heroDetail.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-function drawHeroDetail(px: number, py: number) {
-    const setting = Mota.require('var', 'mainSetting');
-    if (!setting.getValue('screen.heroDetail', false)) return;
-    const { hp, atk, def } = core.status.hero;
-    const toDraw = {
-        atk: {
-            value: atk,
-            color: '#FF7A7A'
-        },
-        def: {
-            value: def,
-            color: '#00E6F1'
-        },
-        hp: {
-            value: hp,
-            color: '#F9FFFF0'
-        }
-    };
-
-    let i = 0;
-    for (const [key, value] of Object.entries(toDraw)) {
-        const ctx = core.canvas['hero'];
-        core.fillBoldText(
-            ctx,
-            core.formatBigNumber(value.value),
-            px,
-            py - 10 * i,
-            value.color
-        );
-        i++;
-    }
-}
-
-export { drawHeroDetail };
diff --git a/src/plugin/game/fx/heroFourFrames.ts b/src/plugin/game/fx/heroFourFrames.ts
deleted file mode 100644
index a1cdf60..0000000
--- a/src/plugin/game/fx/heroFourFrames.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// @ts-nocheck
-
-var heroMoving = timestamp => {
-    if (core.status.heroMoving <= 0) return;
-    if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) {
-        core.animateFrame.leftLeg++;
-        core.animateFrame.moveTime = timestamp;
-    }
-    core.drawHero(
-        ['stop', 'leftFoot', 'midFoot', 'rightFoot'][
-            core.animateFrame.leftLeg % 4
-        ],
-        4 * core.status.heroMoving
-    );
-};
-
-export function init() {
-    ['up', 'down', 'left', 'right'].forEach(one => {
-        // 指定中间帧动画
-        core.material.icons.hero[one].midFoot = 2;
-    });
-
-    core.registerAnimationFrame('heroMoving', true, heroMoving);
-
-    core.events._eventMoveHero_moving = function (step, moveSteps) {
-        var curr = moveSteps[0];
-        var direction = curr[0],
-            x = core.getHeroLoc('x'),
-            y = core.getHeroLoc('y');
-        // ------ 前进/后退
-        var o = direction == 'backward' ? -1 : 1;
-        if (direction == 'forward' || direction == 'backward')
-            direction = core.getHeroLoc('direction');
-        var faceDirection = direction;
-        if (direction == 'leftup' || direction == 'leftdown')
-            faceDirection = 'left';
-        if (direction == 'rightup' || direction == 'rightdown')
-            faceDirection = 'right';
-        core.setHeroLoc('direction', direction);
-        if (curr[1] <= 0) {
-            core.setHeroLoc('direction', faceDirection);
-            moveSteps.shift();
-            return true;
-        }
-        if (step <= 4) core.drawHero('stop', 4 * o * step);
-        else if (step <= 8) core.drawHero('leftFoot', 4 * o * step);
-        else if (step <= 12) core.drawHero('midFoot', 4 * o * (step - 8));
-        else if (step <= 16) core.drawHero('rightFoot', 4 * o * (step - 8)); // if (step == 8) {
-        if (step == 8 || step == 16) {
-            core.setHeroLoc('x', x + o * core.utils.scan2[direction].x, true);
-            core.setHeroLoc('y', y + o * core.utils.scan2[direction].y, true);
-            core.updateFollowers();
-            curr[1]--;
-            if (curr[1] <= 0) moveSteps.shift();
-            core.setHeroLoc('direction', faceDirection);
-            return step == 16;
-        }
-        return false;
-    };
-}
diff --git a/src/plugin/game/fx/itemDetail.ts b/src/plugin/game/fx/itemDetail.ts
index 8ab4cb7..ba3812d 100644
--- a/src/plugin/game/fx/itemDetail.ts
+++ b/src/plugin/game/fx/itemDetail.ts
@@ -27,118 +27,7 @@ export function init() {
 
         // floor.enemy.render(true);
 
-        getItemDetail(floorId, onMap); // 宝石血瓶详细信息
+        // getItemDetail(floorId, onMap); // 宝石血瓶详细信息
         // this.drawDamage(ctx, floorId);
     };
 }
-
-// 获取宝石信息 并绘制
-function getItemDetail(floorId: FloorIds, onMap: boolean) {
-    const setting = Mota.require('var', 'mainSetting');
-    if (!setting.getValue('screen.itemDetail')) return;
-    floorId ??= core.status.thisMap.floorId;
-    let diff: Record<string | symbol, number | undefined> = {};
-    const before = core.status.hero;
-    const hero = core.clone(core.status.hero);
-    const handler: ProxyHandler<any> = {
-        set(target, key, v) {
-            diff[key] = v - (target[key] || 0);
-            if (!diff[key]) diff[key] = void 0;
-            return true;
-        }
-    };
-    core.status.hero = new Proxy(hero, handler);
-
-    core.status.maps[floorId].blocks.forEach(function (block) {
-        if (block.event.cls !== 'items' || block.disable) return;
-        const x = block.x,
-            y = block.y;
-        // v2优化,只绘制范围内的部分
-        if (onMap && core.bigmap.v2) {
-            if (
-                x < core.bigmap.posX - core.bigmap.extend ||
-                x > core.bigmap.posX + core._PX_ + core.bigmap.extend ||
-                y < core.bigmap.posY - core.bigmap.extend ||
-                y > core.bigmap.posY + core._PY_ + core.bigmap.extend
-            ) {
-                return;
-            }
-        }
-        diff = {};
-        const id = block.event.id as AllIdsOf<'items'>;
-        const item = core.material.items[id];
-        if (item.cls === 'equips') {
-            // 装备也显示
-            const diff: Record<string, any> = core.clone(
-                item.equip.value ?? {}
-            );
-            const per = item.equip.percentage ?? {};
-            for (const name in per) {
-                diff[name + 'per'] =
-                    per[name as SelectKey<HeroStatus, number>].toString() + '%';
-            }
-            drawItemDetail(diff, x, y);
-            return;
-        }
-        // 跟数据统计原理一样 执行效果 前后比较
-        core.setFlag('__statistics__', true);
-        try {
-            eval(item.itemEffect!);
-        } catch (error) {}
-        drawItemDetail(diff, x, y);
-    });
-    core.status.hero = before;
-    window.hero = before;
-    window.flags = before.flags;
-}
-
-// 绘制
-function drawItemDetail(diff: any, x: number, y: number) {
-    const px = 32 * x + 2,
-        py = 32 * y + 31;
-    let content = '';
-    // 获得数据和颜色
-    let i = 0;
-    for (const name in diff) {
-        if (!diff[name]) continue;
-        let color = '#fff';
-
-        if (typeof diff[name] === 'number')
-            content = core.formatBigNumber(Math.round(diff[name]), true);
-        else content = diff[name];
-
-        switch (name) {
-            case 'atk':
-            case 'atkper':
-                color = '#FF7A7A';
-                break;
-            case 'def':
-            case 'defper':
-                color = '#00E6F1';
-                break;
-            case 'mdef':
-            case 'mdefper':
-                color = '#6EFF83';
-                break;
-            case 'hp':
-                color = '#A4FF00';
-                break;
-            case 'hpmax':
-            case 'hpmaxper':
-                color = '#F9FF00';
-                break;
-            case 'manaper':
-            case 'mana':
-                color = '#c66';
-                break;
-        }
-        // 绘制
-        core.status.damage.data.push({
-            text: content,
-            px: px,
-            py: py - 10 * i,
-            color: color as Color
-        });
-        i++;
-    }
-}
diff --git a/src/plugin/game/index.ts b/src/plugin/game/index.ts
index 6ce77c3..c5f201b 100644
--- a/src/plugin/game/index.ts
+++ b/src/plugin/game/index.ts
@@ -1,11 +1,9 @@
 /* @__PURE__ */ import './dev/hotReload'; // 仅开发会用到
 import * as fiveLayer from './fiveLayer';
-import * as heroFourFrames from './fx/heroFourFrames';
 import * as itemDetail from './fx/itemDetail';
 import * as replay from './replay';
 import * as ui from './ui';
 import * as rewrite from './fx/rewrite';
-import * as halo from './fx/halo';
 import * as loopMap from './loopMap';
 import * as removeMap from './removeMap';
 import * as shop from './shop';
@@ -29,10 +27,8 @@ Mota.Plugin.register('chase_g', chase);
 Mota.Plugin.register('skill_g', skill);
 Mota.Plugin.register('towerBoss_g', towerBoss);
 Mota.Plugin.register('fiveLayer_g', fiveLayer, fiveLayer.init);
-Mota.Plugin.register('heroFourFrames_g', heroFourFrames, heroFourFrames.init);
 Mota.Plugin.register('rewrite_g', rewrite, rewrite.init);
 Mota.Plugin.register('itemDetail_g', itemDetail, itemDetail.init);
-Mota.Plugin.register('halo_g', halo);
 // Mota.Plugin.register('study_g', study);
 Mota.Plugin.register('remainEnemy_g', remainEnemy);
 Mota.Plugin.register('checkBlock_g', checkBlock, checkBlock.init);
diff --git a/src/plugin/layout/layout.ts b/src/plugin/layout/layout.ts
deleted file mode 100644
index 262818d..0000000
--- a/src/plugin/layout/layout.ts
+++ /dev/null
@@ -1,345 +0,0 @@
-import { has } from '../utils';
-
-type CanvasStyle = string | CanvasPattern | CanvasGradient;
-
-export class Layout {
-    /** 画布 */
-    canvas: HTMLCanvasElement;
-    /** 绘制上下文 */
-    ctx: CanvasRenderingContext2D;
-
-    static readonly CLEAR: number = 1;
-    static readonly MASK: number = 2;
-    static readonly IMAGE: number = 4;
-
-    static readonly FILL: number = 1;
-    static readonly STROKE: number = 2;
-
-    constructor(canvas: HTMLCanvasElement) {
-        this.canvas = canvas;
-        this.ctx = canvas.getContext('2d')!;
-    }
-
-    image(layout: Layout | CanvasImageSource | Path2D, type: number): this;
-    image(
-        layout: Layout | CanvasImageSource | Path2D,
-        type: number,
-        x: number,
-        y: number
-    ): this;
-    image(
-        layout: Layout | CanvasImageSource | Path2D,
-        type: number,
-        x: number,
-        y: number,
-        w: number,
-        h: number
-    ): this;
-    image(
-        layout: Layout | CanvasImageSource | Path2D,
-        type: number,
-        sx: number,
-        sy: number,
-        sw: number,
-        sh: number,
-        dx: number,
-        dy: number,
-        dw: number,
-        dh: number
-    ): this;
-    image(
-        layout: Layout | CanvasImageSource | Path2D,
-        type: number,
-        sx: number = 0,
-        sy: number = 0,
-        sw?: number,
-        sh?: number,
-        dx?: number,
-        dy?: number,
-        dw?: number,
-        dh?: number
-    ) {
-        const img = layout instanceof Layout ? layout.canvas : layout;
-        const fill = () => {
-            if (img instanceof Path2D) {
-                this.ctx.fill(img);
-            } else {
-                if (!has(sw)) {
-                    this.ctx.drawImage(img, sx, sy);
-                } else if (!has(dx)) {
-                    this.ctx.drawImage(img, sx, sy, sw, sh!);
-                } else {
-                    this.ctx.drawImage(img, sx, sy, sw, sh!, dx, dy!, dw!, dh!);
-                }
-            }
-        };
-        if (type & Layout.IMAGE) {
-            // 绘制图片
-            fill();
-        }
-        if (type & Layout.CLEAR) {
-            // 按照图片清除一个区域
-            this.ctx.save();
-            this.ctx.globalCompositeOperation = 'destination-out';
-            fill();
-            this.ctx.restore();
-        }
-        if (type & Layout.MASK) {
-            // 蒙版,只显示蒙版内的东西
-            this.ctx.save();
-            this.ctx.globalCompositeOperation = 'destination-in';
-            fill();
-            this.ctx.restore();
-        }
-        return this;
-    }
-
-    /**
-     * 擦除一个矩形
-     */
-    clear(x: number, y: number, w: number, h: number): this {
-        this.ctx.clearRect(x, y, w, h);
-        return this;
-    }
-
-    /**
-     * 绘制文字
-     * @param str 文字
-     * @param type 绘制类型,FILL表示填充,STROKE表示描边,FILL | STROKE 表示既填充又描边
-     * @param x 横坐标
-     * @param y 纵坐标
-     * @param maxWidth 最大宽度
-     */
-    text(
-        str: string,
-        type: number,
-        x: number,
-        y: number,
-        maxWidth?: number
-    ): this {
-        if (type & Layout.FILL) this.ctx.fillText(str, x, y, maxWidth);
-        if (type & Layout.STROKE) this.ctx.strokeText(str, x, y, maxWidth);
-        return this;
-    }
-
-    /**
-     * 根据路径进行绘制
-     * @param path 路径
-     * @param type 绘制类型
-     */
-    path(path: Path2D, type: number, rule?: CanvasFillRule): this {
-        if (type & Layout.FILL) this.ctx.fill(path, rule);
-        if (type & Layout.STROKE) this.ctx.stroke(path);
-        return this;
-    }
-
-    /**
-     * 保存画布状态
-     */
-    save(): this {
-        this.ctx.save();
-        return this;
-    }
-
-    /**
-     * 回退画布状态
-     */
-    restore(): this {
-        this.ctx.restore();
-        return this;
-    }
-
-    /**
-     * 设置填充样式
-     * @param style 样式
-     */
-    fillStyle(style: CanvasStyle): this {
-        this.ctx.fillStyle = style;
-        return this;
-    }
-
-    /**
-     * 设置描边样式
-     * @param style 样式
-     */
-    strokeStyle(style: CanvasStyle): this {
-        this.ctx.strokeStyle = style;
-        return this;
-    }
-
-    /**
-     * 设置文本对齐
-     * @param align 文本左右对齐方式
-     */
-    textAlign(align: CanvasTextAlign): this {
-        this.ctx.textAlign = align;
-        return this;
-    }
-
-    /**
-     * 设置文本基线
-     * @param align 文本基线,即文本上下对齐方式
-     */
-    textBaseline(align: CanvasTextBaseline): this {
-        this.ctx.textBaseline = align;
-        return this;
-    }
-
-    /**
-     * 设置滤镜
-     * @param filter 滤镜
-     */
-    filter(filter: string): this {
-        this.ctx.filter = filter;
-        return this;
-    }
-
-    /**
-     * 设置阴影信息
-     * @param shadow 阴影信息
-     */
-    shadow(shadow: Partial<CanvasShadowStyles>): this {
-        for (const [p, v] of Object.entries(shadow)) {
-            // @ts-ignore
-            this.ctx[p] = v;
-        }
-        return this;
-    }
-
-    /**
-     * 设置线宽(描边宽度,包括字体描边)
-     * @param width 宽度
-     */
-    lineWidth(width: number): this {
-        this.ctx.lineWidth = width;
-        return this;
-    }
-
-    /**
-     * 设置线尾样式
-     * @param cap 线尾样式
-     */
-    lineCap(cap: CanvasLineCap): this {
-        this.ctx.lineCap = cap;
-        return this;
-    }
-
-    /**
-     * 设置线段连接方式样式
-     * @param join 线段连接方式
-     */
-    lineJoin(join: CanvasLineJoin): this {
-        this.ctx.lineJoin = join;
-        return this;
-    }
-
-    /**
-     * 设置画布的字体
-     * @param font 字体
-     */
-    font(font: string): this {
-        this.ctx.font = font;
-        return this;
-    }
-
-    /**
-     * 设置画布之后绘制的不透明度
-     * @param alpha 不透明度
-     */
-    alpha(alpha: number): this {
-        this.ctx.globalAlpha = alpha;
-        return this;
-    }
-
-    /**
-     * 设置虚线样式
-     * @param dash 虚线样式
-     */
-    lineDash(dash: number[]): this {
-        this.ctx.setLineDash(dash);
-        return this;
-    }
-
-    /**
-     * 放缩画布
-     * @param x 横向放缩量
-     * @param y 纵向放缩量
-     */
-    scale(x: number, y: number): this {
-        this.ctx.scale(x, y);
-        return this;
-    }
-
-    /**
-     * 旋转画布
-     * @param rad 顺时针旋转的弧度数
-     */
-    rotate(rad: number): this {
-        this.ctx.rotate(rad);
-        return this;
-    }
-
-    /**
-     * 平移画布
-     * @param x 水平平移量
-     * @param y 竖直平移量
-     */
-    translate(x: number, y: number): this {
-        this.ctx.translate(x, y);
-        return this;
-    }
-
-    /**
-     * 重设变换矩阵
-     */
-    transform(): Layout;
-    /**
-     * 叠加变换矩阵(当前画布的矩阵乘以传入的矩阵)
-     * 矩阵说明:
-     * [a c e]
-     * [b d f]
-     * [0 0 0]
-     * @param a 水平缩放
-     * @param b 垂直倾斜
-     * @param c 水平倾斜
-     * @param d 垂直缩放
-     * @param e 水平移动
-     * @param f 垂直移动
-     */
-    transform(
-        a: number,
-        b: number,
-        c: number,
-        d: number,
-        e: number,
-        f: number
-    ): Layout;
-    transform(
-        a?: number,
-        b?: number,
-        c?: number,
-        d?: number,
-        e?: number,
-        f?: number
-    ) {
-        if (!has(a)) this.ctx.resetTransform();
-        else this.ctx.transform(a, b!, c!, d!, e!, f!);
-        return this;
-    }
-
-    /**
-     * 设置混合方式,像image的蒙版功能与擦除功能本质上也是通过设置混合方式实现的
-     * @param value 混合方式
-     */
-    composite(value: GlobalCompositeOperation): this {
-        this.ctx.globalCompositeOperation = value;
-        return this;
-    }
-
-    /**
-     * 删除这个布局
-     */
-    destroy() {
-        this.canvas.remove();
-    }
-}
diff --git a/src/plugin/layout/layoutGame.ts b/src/plugin/layout/layoutGame.ts
deleted file mode 100644
index 7e84604..0000000
--- a/src/plugin/layout/layoutGame.ts
+++ /dev/null
@@ -1,255 +0,0 @@
-import { Layout } from './layout';
-
-export class LayoutGame extends Layout {
-    /** 画布id */
-    id: string;
-
-    constructor(
-        id: string,
-        x: number,
-        y: number,
-        w: number,
-        h: number,
-        z: number
-    ) {
-        const ctx = core.createCanvas(id, x, y, w, h, z);
-        super(ctx.canvas);
-        this.id = id;
-    }
-
-    image2(
-        layout: Layout | CanvasImageSource | Path2D | ImageIds,
-        type: number
-    ): LayoutGame;
-    image2(
-        layout: Layout | CanvasImageSource | Path2D | ImageIds,
-        type: number,
-        x: number,
-        y: number
-    ): LayoutGame;
-    image2(
-        layout: Layout | CanvasImageSource | Path2D | ImageIds,
-        type: number,
-        x: number,
-        y: number,
-        w: number,
-        h: number
-    ): LayoutGame;
-    image2(
-        layout: Layout | CanvasImageSource | Path2D | ImageIds,
-        type: number,
-        sx: number,
-        sy: number,
-        sw: number,
-        sh: number,
-        dx: number,
-        dy: number,
-        dw: number,
-        dh: number
-    ): LayoutGame;
-    image2(
-        layout: Layout | CanvasImageSource | Path2D | ImageIds,
-        type: number,
-        sx?: number,
-        sy?: number,
-        sw?: number,
-        sh?: number,
-        dx?: number,
-        dy?: number,
-        dw?: number,
-        dh?: number
-    ): this {
-        const img =
-            typeof layout === 'string'
-                ? core.material.images.images[layout]
-                : layout;
-
-        this.image(img, type, sx!, sy!, sw!, sh!, dx!, dy!, dw!, dh!);
-        return this;
-    }
-
-    /**
-     * 绘制一个图标
-     * @param id 图标id
-     * @param x 横坐标
-     * @param y 纵坐标
-     * @param w 宽度
-     * @param h 高度
-     * @param frame 绘制第几帧
-     */
-    icon(
-        id: AllIds,
-        x: number,
-        y: number,
-        w?: number,
-        h?: number,
-        frame?: number
-    ): this {
-        core.drawIcon(this.ctx, id, x, y, w, h, frame);
-        return this;
-    }
-
-    /**
-     * 绘制WindowSkin
-     * @param direction 指向箭头的方向
-     */
-    winskin(
-        background: any,
-        x: number,
-        y: number,
-        w: number,
-        h: number,
-        direction?: 'up' | 'down',
-        px?: number,
-        py?: number
-    ): this {
-        core.drawWindowSkin(
-            background,
-            this.ctx,
-            x,
-            y,
-            w,
-            h,
-            direction,
-            px,
-            py
-        );
-        return this;
-    }
-
-    /**
-     * 绘制一个箭头
-     * @param x1 起始点横坐标
-     * @param y1 起始点纵坐标
-     * @param x2 终止点横坐标
-     * @param y2 终止点纵坐标
-     */
-    arrow(x1: number, y1: number, x2: number, y2: number): this {
-        core.drawArrow(this.ctx, x1, y1, x2, y2);
-        return this;
-    }
-
-    /**
-     * 绘制线段
-     */
-    line(x1: number, y1: number, x2: number, y2: number): this {
-        this.ctx.beginPath();
-        this.ctx.moveTo(x1, y1);
-        this.ctx.lineTo(x2, y2);
-        this.ctx.stroke();
-        return this;
-    }
-
-    /**
-     * 绘制圆弧或扇形
-     * @param type 绘制类型
-     * @param start 起始弧度
-     * @param end 终止弧度
-     * @param anticlockwise 是否逆时针
-     */
-    arc(
-        type: number,
-        x: number,
-        y: number,
-        r: number,
-        start: number,
-        end: number,
-        anticlockwise: boolean = false
-    ): this {
-        this.ctx.beginPath();
-        this.ctx.arc(x, y, r, start, end, anticlockwise);
-        this.draw(type);
-        return this;
-    }
-
-    /**
-     * 绘制一个圆
-     * @param type 绘制类型
-     */
-    circle(type: number, x: number, y: number, r: number): this {
-        return this.arc(type, x, y, r, 0, Math.PI * 2);
-    }
-
-    /**
-     * 绘制椭圆
-     */
-    ellipse(
-        type: number,
-        x: number,
-        y: number,
-        a: number,
-        b: number,
-        rotation: number = 0,
-        start: number = 0,
-        end: number = Math.PI * 2,
-        anticlockwise: boolean = false
-    ): this {
-        this.ctx.beginPath();
-        this.ctx.ellipse(x, y, a, b, rotation, start, end, anticlockwise);
-        this.draw(type);
-        return this;
-    }
-
-    /**
-     * 绘制多边形
-     * @param type 绘制类型
-     * @param nodes 多边形节点
-     * @param close 是否闭合路径
-     */
-    polygon(type: number, nodes: LocArr[], close: boolean = true): this {
-        this.ctx.beginPath();
-        this.ctx.moveTo(nodes[0][0], nodes[0][1]);
-        for (let i = 1; i < nodes.length; i++) {
-            this.ctx.lineTo(nodes[i][0], nodes[i][1]);
-        }
-        if (close) this.ctx.closePath();
-        this.draw(type);
-        return this;
-    }
-
-    /**
-     * 绘制矩形
-     */
-    rect(type: number, x: number, y: number, w: number, h: number): this {
-        if (type & Layout.FILL) this.ctx.fillRect(x, y, w, h);
-        if (type & Layout.STROKE) this.ctx.strokeRect(x, y, w, h);
-        return this;
-    }
-
-    /**
-     * 绘制圆角矩形
-     */
-    roundRect(
-        type: number,
-        x: number,
-        y: number,
-        w: number,
-        h: number,
-        r: number
-    ) {
-        this.ctx.beginPath();
-        this.ctx.moveTo(x + r, y);
-        this.ctx.lineTo(x + w - r, y);
-        this.ctx.quadraticCurveTo(x + w, y, x + w, y + r);
-        this.ctx.lineTo(x + w, y + h - r);
-        this.ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
-        this.ctx.lineTo(x + r, y + h);
-        this.ctx.quadraticCurveTo(x, y + h, x, y + h - r);
-        this.ctx.lineTo(x, y + r);
-        this.ctx.quadraticCurveTo(x, y, x + r, y);
-        this.ctx.closePath();
-        this.draw(type);
-    }
-
-    /**
-     * 删除这个布局
-     */
-    destroy() {
-        core.deleteCanvas(this.id);
-    }
-
-    private draw(type: number) {
-        if (type & Layout.FILL) this.ctx.fill();
-        if (type & Layout.STROKE) this.ctx.stroke();
-    }
-}
diff --git a/src/plugin/shadow/gameShadow.ts b/src/plugin/shadow/gameShadow.ts
deleted file mode 100644
index d0ec7a7..0000000
--- a/src/plugin/shadow/gameShadow.ts
+++ /dev/null
@@ -1,243 +0,0 @@
-import { Polygon } from './polygon';
-import {
-    Light,
-    getAllLights,
-    initShadowCanvas,
-    refreshLight,
-    removeAllLights,
-    setBackground,
-    setBlur,
-    setLightList,
-    setShadowNodes
-} from './shadow';
-import { pColor } from '../utils';
-import { setCanvasFilterByFloorId } from '../fx/gameCanvas';
-
-export function init() {
-    // 勇士身上的光源
-    // Mota.rewrite(core.control, 'drawHero', 'add', () => {
-    //     if (core.getFlag('__heroOpacity__') !== 0) {
-    //         getAllLights().forEach(v => {
-    //             if (!v.followHero) return;
-    //             v._offset ??= { x: v.x, y: v.y };
-    //             v.x = core.status.heroCenter.px + v._offset.x;
-    //             v.y = core.status.heroCenter.py + v._offset.y;
-    //             refreshLight();
-    //         });
-    //     }
-    // });
-    // // 更新地形数据
-    // Mota.rewrite(core.maps, 'removeBlock', 'add', success => {
-    //     if (success && main.replayChecking) updateShadow(true);
-    //     return success;
-    // });
-    // Mota.rewrite(core.maps, 'setBlock', 'add', () => {
-    //     if (main.replayChecking) updateShadow(true);
-    // });
-    // Mota.rewrite(core.events, 'changingFloor', 'add', (_, floorId) => {
-    //     if (!main.replayChecking) {
-    //         updateShadow();
-    //         setCanvasFilterByFloorId(floorId);
-    //     }
-    // });
-    // // 初始化画布信息
-    // Mota.rewrite(core.ui, 'deleteAllCanvas', 'add', () => {
-    //     if (main.mode === 'play' && !main.replayChecking) initShadowCanvas();
-    // });
-}
-
-const shadowInfo: Partial<Record<FloorIds, Light[]>> = {
-    MT50: [
-        {
-            id: 'mt50_1',
-            x: 144,
-            y: 144,
-            decay: 20,
-            r: 150,
-            color: pColor('#e953'),
-            noShelter: true
-        },
-        {
-            id: 'mt50_2',
-            x: 336,
-            y: 144,
-            decay: 20,
-            r: 150,
-            color: pColor('#e953'),
-            noShelter: true
-        },
-        {
-            id: 'mt50_2',
-            x: 336,
-            y: 336,
-            decay: 20,
-            r: 150,
-            color: pColor('#e953'),
-            noShelter: true
-        },
-        {
-            id: 'mt50_2',
-            x: 144,
-            y: 336,
-            decay: 20,
-            r: 150,
-            color: pColor('#e953'),
-            noShelter: true
-        }
-    ],
-    MT52: [
-        {
-            id: 'mt51_1',
-            x: 13 * 32 + 16,
-            y: 6 * 32 + 16,
-            decay: 20,
-            r: 250,
-            color: pColor('#e953')
-        },
-        {
-            id: 'mt51_2',
-            x: 1 * 32 + 16,
-            y: 13 * 32 + 16,
-            decay: 20,
-            r: 350,
-            color: pColor('#e953')
-        }
-    ]
-};
-const backgroundInfo: Partial<Record<FloorIds, Color>> = {
-    MT50: pColor('#0006'),
-    MT51: pColor('#0004'),
-    MT52: pColor('#0004')
-};
-const blurInfo: Partial<Record<FloorIds, number>> = {};
-const immersionInfo: Partial<Record<FloorIds, number>> = {};
-const shadowCache: Partial<Record<FloorIds, Polygon[]>> = {};
-
-let calMapShadow = true;
-
-Mota.require('var', 'loading').once('coreInit', () => {
-    core.floorIds.slice(61).forEach(v => {
-        shadowInfo[v] ??= [];
-        shadowInfo[v]?.push({
-            id: `${v}_hero`,
-            x: 0,
-            y: 0,
-            decay: 50,
-            r: 300,
-            color: 'transparent',
-            followHero: true
-        });
-        core.floors[v].map.forEach((arr, y) => {
-            arr.forEach((num, x) => {
-                if (num === 103) {
-                    shadowInfo[v]?.push({
-                        id: `${v}_${x}_${y}`,
-                        x: x * 32 + 16,
-                        y: y * 32 + 16,
-                        decay: 20,
-                        r: 350,
-                        color: pColor('#e953')
-                    });
-                }
-            });
-        });
-    });
-});
-
-export function updateShadow(nocache: boolean = false) {
-    // todo: 需要优化,优化成bfs
-    const floor = core.status.floorId;
-    if (!shadowInfo[floor] || !backgroundInfo[floor]) {
-        removeAllLights();
-        setShadowNodes([]);
-        setBackground('transparent');
-        return;
-    }
-    const f = core.status.thisMap;
-    const w = f.width;
-    const h = f.height;
-    const nodes: Polygon[] = [];
-    if (calMapShadow) {
-        if (shadowCache[floor] && !nocache) {
-            setShadowNodes(shadowCache[floor]!);
-        } else {
-            core.extractBlocks();
-            const blocks = core.getMapBlocksObj();
-            core.status.maps[floor].blocks.forEach(v => {
-                if (
-                    !['terrains', 'autotile', 'tileset', 'animates'].includes(
-                        v.event.cls
-                    )
-                ) {
-                    return;
-                }
-
-                if (v.event.noPass) {
-                    const immerse = immersionInfo[floor] ?? 4;
-                    const x = v.x;
-                    const y = v.y;
-                    let left = x * 32 + immerse;
-                    let top = y * 32 + immerse;
-                    let right = left + 32 - immerse * 2;
-                    let bottom = top + 32 - immerse * 2;
-                    const l: LocString = `${x - 1},${y}`;
-                    const r: LocString = `${x + 1},${y}`;
-                    const t: LocString = `${x},${y - 1}`;
-                    const b: LocString = `${x},${y + 1}`;
-
-                    if (x === 0) left -= immerse * 2;
-                    if (x + 1 === w) right += immerse * 2;
-                    if (y === 0) top -= immerse * 2;
-                    if (y + 1 === h) bottom += immerse * 2;
-
-                    if (blocks[l] && blocks[l].event.noPass) {
-                        left -= immerse;
-                    }
-                    if (blocks[r] && blocks[r].event.noPass) {
-                        right += immerse;
-                    }
-                    if (blocks[t] && blocks[t].event.noPass) {
-                        top -= immerse;
-                    }
-                    if (blocks[b] && blocks[b].event.noPass) {
-                        bottom += immerse;
-                    }
-                    nodes.push(
-                        new Polygon([
-                            [left, top],
-                            [right, top],
-                            [right, bottom],
-                            [left, bottom]
-                        ])
-                    );
-                    return;
-                }
-            });
-            shadowCache[floor] = nodes;
-            setShadowNodes(nodes);
-        }
-    } else {
-        setShadowNodes([]);
-        setBlur(0);
-    }
-    setLightList(shadowInfo[floor]!);
-    setBackground(backgroundInfo[floor]!);
-    setBlur(blurInfo[floor] ?? 3);
-}
-
-/**
- * 清除某一层的墙壁缓存
- * @param floorId 楼层id
- */
-export function clearShadowCache(floorId: FloorIds) {
-    delete shadowCache[floorId];
-}
-
-/**
- * 设置是否不计算墙壁遮挡,对所有灯光有效
- * @param n 目标值
- */
-export function setCalShadow(n: boolean) {
-    calMapShadow = n;
-    updateShadow();
-}
diff --git a/src/plugin/shadow/polygon.ts b/src/plugin/shadow/polygon.ts
deleted file mode 100644
index 1a031d2..0000000
--- a/src/plugin/shadow/polygon.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-export class Polygon {
-    /**
-     * 多边形的节点
-     */
-    nodes: LocArr[];
-
-    private cache: Record<string, LocArr[][]> = {};
-
-    static from(...polygons: LocArr[][]) {
-        return polygons.map(v => new Polygon(v));
-    }
-
-    constructor(nodes: LocArr[]) {
-        if (nodes.length < 3) {
-            throw new Error(`Nodes number delivered is less than 3!`);
-        }
-        this.nodes = nodes.map(v => [v[0] + 32, v[1] + 32]);
-    }
-
-    /**
-     * 获取一个点光源下的阴影
-     */
-    shadowArea(x: number, y: number, r: number): LocArr[][] {
-        const id = `${x},${y}`;
-        if (this.cache[id]) return this.cache[id];
-        const res: LocArr[][] = [];
-        const w = (core._PX_ ?? core.__PIXELS__) + 64;
-        const h = (core._PY_ ?? core.__PIXELS__) + 64;
-
-        const aspect = h / w;
-
-        const intersect = (nx: number, ny: number): LocArr => {
-            const k = (ny - y) / (nx - x);
-            if (k > aspect || k < -aspect) {
-                if (ny < y) {
-                    const ix = x + y / k;
-                    return [2 * x - ix, 0];
-                } else {
-                    const ix = x + (h - y) / k;
-                    return [ix, h];
-                }
-            } else {
-                if (nx < x) {
-                    const iy = y + k * x;
-                    return [0, 2 * y - iy];
-                } else {
-                    const iy = y + k * (w - x);
-                    return [w, iy];
-                }
-            }
-        };
-        const l = this.nodes.length;
-        let now = intersect(...this.nodes[0]);
-        for (let i = 0; i < l; i++) {
-            const next = (i + 1) % l;
-            const nextInter = intersect(...this.nodes[next]);
-            const start = [this.nodes[i], now];
-            const end = [nextInter, this.nodes[next]];
-            let path: LocArr[];
-            if (
-                (now[0] === 0 && nextInter[1] === 0) ||
-                (now[1] === 0 && nextInter[0] === 0)
-            ) {
-                path = [...start, [0, 0], ...end];
-            } else if (
-                (now[0] === 0 && nextInter[1] === h) ||
-                (now[1] === h && nextInter[0] === 0)
-            ) {
-                path = [...start, [0, h], ...end];
-            } else if (
-                (now[0] === w && nextInter[1] === 0) ||
-                (now[1] === 0 && nextInter[0] === w)
-            ) {
-                path = [...start, [w, 0], ...end];
-            } else if (
-                (now[0] === w && nextInter[1] === h) ||
-                (now[1] === h && nextInter[0] === w)
-            ) {
-                path = [...start, [w, h], ...end];
-            } else {
-                path = [...start, ...end];
-            }
-            res.push(path);
-            now = nextInter;
-        }
-
-        this.cache[id] = res;
-
-        return res;
-    }
-}
diff --git a/src/plugin/shadow/shadow.ts b/src/plugin/shadow/shadow.ts
deleted file mode 100644
index a28234e..0000000
--- a/src/plugin/shadow/shadow.ts
+++ /dev/null
@@ -1,468 +0,0 @@
-import {
-    Animation,
-    linear,
-    PathFn,
-    TimingFn,
-    Transition
-} from 'mutate-animate';
-import { has } from '../utils';
-import { Polygon } from './polygon';
-
-interface TransitionInfo {
-    time: number;
-    mode: TimingFn;
-}
-
-export interface Light {
-    id: string;
-    x: number;
-    y: number;
-    r: number;
-    /** 衰减开始半径 */
-    decay: number;
-    /** 颜色,每个值的范围0.0~1.0 */
-    color: Color;
-    /** 是否可以被物体遮挡 */
-    noShelter?: boolean;
-    /** 是否跟随勇士 */
-    followHero?: boolean;
-    /** 正在动画的属性 */
-    _animating?: Record<string, boolean>;
-    /** 执行渐变的属性 */
-    _transition?: Record<string, TransitionInfo>;
-    /** 表示是否是代理,只有设置渐变后才会变为true */
-    _isProxy?: boolean;
-    /** 跟随勇士的时候的偏移量 */
-    _offset?: Loc;
-}
-
-export function init() {
-    core.registerAnimationFrame('shadow', true, () => {
-        if (!needRefresh) return;
-        drawShadow();
-    });
-}
-
-let canvas: HTMLCanvasElement;
-let ctx: CanvasRenderingContext2D;
-let lights: Light[] = [];
-let needRefresh = false;
-let shadowNodes: Polygon[] = [];
-let background: Color;
-let blur = 3;
-const temp1 = document.createElement('canvas');
-const temp2 = document.createElement('canvas');
-const temp3 = document.createElement('canvas');
-const ct1 = temp1.getContext('2d')!;
-const ct2 = temp2.getContext('2d')!;
-const ct3 = temp3.getContext('2d')!;
-
-const animationList: Record<string, Animation> = {};
-const transitionList: Record<string, Transition> = {};
-
-/**
- * 初始化阴影画布
- */
-export function initShadowCanvas() {
-    const w = core._PX_ ?? core.__PIXELS__;
-    const h = core._PY_ ?? core.__PIXELS__;
-    ctx = core.createCanvas('shadow', -32, -32, w + 64, h + 64, 55);
-    canvas = ctx.canvas;
-
-    const s = core.domStyle.scale * devicePixelRatio;
-    temp1.width = (w + 64) * s;
-    temp1.height = (h + 64) * s;
-    temp2.width = (w + 64) * s;
-    temp2.height = (h + 64) * s;
-    temp3.width = (w + 64) * s;
-    temp3.height = (h + 64) * s;
-    ct1.scale(s, s);
-    ct2.scale(s, s);
-    ct3.scale(s, s);
-    canvas.style.filter = `blur(${blur}px)`;
-}
-
-/**
- * 添加一个光源
- * @param info 光源信息
- */
-export function addLight(info: Light) {
-    lights.push(info);
-    needRefresh = true;
-}
-
-/**
- * 移除一个光源
- * @param id 光源id
- */
-export function removeLight(id: string) {
-    const index = lights.findIndex(v => v.id === id);
-    if (index === -1) {
-        throw new ReferenceError(`You are going to remove nonexistent light!`);
-    }
-    lights.splice(index, 1);
-    needRefresh = true;
-}
-
-/**
- * 设置一个光源的信息
- * @param id 光源id
- * @param info 光源信息
- */
-export function setLight(id: string, info: Partial<Light>) {
-    if (has(info.id)) delete info.id;
-    const light = lights.find(v => v.id === id);
-    if (!light) {
-        throw new ReferenceError(`You are going to set nonexistent light!`);
-    }
-    for (const [p, v] of Object.entries(info)) {
-        light[p as SelectKey<Light, number>] = v as number;
-    }
-    needRefresh = true;
-}
-
-/**
- * 设置当前的光源列表
- * @param list 光源列表
- */
-export function setLightList(list: Light[]) {
-    lights = list;
-    needRefresh = true;
-}
-
-/**
- * 去除所有的光源
- */
-export function removeAllLights() {
-    lights = [];
-    needRefresh = true;
-}
-
-/**
- * 获取一个灯光
- * @param id 灯光id
- */
-export function getLight(id: string) {
-    return lights.find(v => v.id === id);
-}
-
-/**
- * 获取所有灯光
- */
-export function getAllLights() {
-    return lights;
-}
-
-/**
- * 设置背景色
- * @param color 背景色
- */
-export function setBackground(color: Color) {
-    background = color;
-    needRefresh = true;
-}
-
-/**
- * 刷新灯光信息并重绘
- */
-export function refreshLight() {
-    needRefresh = true;
-}
-
-/**
- * 动画改变一个属性的值
- * @param id 灯光id
- * @param key 动画属性,x,y,r,decay,颜色请使用animateLightColor(下个版本会加)
- * @param n 目标值
- * @param time 动画时间
- * @param mode 动画方式,渐变函数,高级动画提供了大量内置的渐变函数
- * @param relative 相对方式,是绝对还是相对
- */
-export function animateLight<K extends Exclude<keyof Light, 'id'>>(
-    id: string,
-    key: K,
-    n: Light[K],
-    time: number = 1000,
-    mode: TimingFn = linear(),
-    relative: boolean = false
-) {
-    const light = getLight(id);
-    if (!has(light)) {
-        throw new ReferenceError(`You are going to animate nonexistent light`);
-    }
-    if (typeof n !== 'number') {
-        light[key] = n;
-    }
-    const ani = animationList[id] ?? (animationList[id] = new Animation());
-    if (typeof ani.value[key] !== 'number') {
-        ani.register(key, light[key] as number);
-    } else {
-        ani.time(0)
-            .mode(linear())
-            .absolute()
-            .apply(key, light[key] as number);
-    }
-    ani.time(time)
-        .mode(mode)
-        [relative ? 'relative' : 'absolute']()
-        .apply(key, n as number);
-    const start = Date.now();
-    const fn = () => {
-        if (Date.now() - start > time + 50) {
-            ani.ticker.remove(fn);
-            light._animating![key] = false;
-        }
-        needRefresh = true;
-        light[key as SelectKey<Light, number>] = ani.value[key];
-    };
-    ani.ticker.add(fn);
-    light._animating ??= {};
-    light._animating[key] = true;
-}
-
-/**
- * 把一个属性设置为渐变模式
- * @param id 灯光id
- * @param key 渐变的属性
- * @param time 渐变时长
- * @param mode 渐变方式,渐变函数,高级动画提供了大量内置的渐变函数
- */
-export function transitionLight<K extends Exclude<keyof Light, 'id'>>(
-    id: string,
-    key: K,
-    time: number = 1000,
-    mode: TimingFn = linear()
-) {
-    const index = lights.findIndex(v => v.id === id);
-    if (index === -1) {
-        throw new ReferenceError(`You are going to transite nonexistent light`);
-    }
-    const light = lights[index];
-    if (typeof light[key] !== 'number') return;
-    light._transition ??= {};
-    light._transition[key] = { time, mode };
-    const tran = transitionList[id] ?? (transitionList[id] = new Transition());
-    tran.value[key] = light[key] as number;
-    if (!light._isProxy) {
-        const handler: ProxyHandler<Light> = {
-            set(t, p, v) {
-                if (typeof p === 'symbol') return false;
-                const start = Date.now();
-                if (
-                    !light._transition![p] ||
-                    light._animating?.[key] ||
-                    typeof v !== 'number'
-                ) {
-                    t[p as SelectKey<Light, number>] = v;
-                    return true;
-                }
-                // @ts-ignore
-                t[p] = light[p];
-                const info = light._transition![p];
-                tran.mode(info.mode).time(info.time);
-                const fn = () => {
-                    if (Date.now() - start > info.time + 50) {
-                        tran.ticker.remove(fn);
-                    }
-                    needRefresh = true;
-                    t[p as SelectKey<Light, number>] = tran.value[key];
-                };
-                tran.ticker.add(fn);
-                tran.transition(p, v);
-                return true;
-            }
-        };
-        lights[index] = new Proxy(light, handler);
-    }
-}
-
-/**
- * 移动一个灯光
- * @param id 灯光id
- * @param x 目标横坐标
- * @param y 目标纵坐标
- * @param time 移动时间
- * @param mode 移动方式,渐变函数
- * @param relative 相对模式,相对还是绝对
- */
-export function moveLight(
-    id: string,
-    x: number,
-    y: number,
-    time: number = 1000,
-    mode: TimingFn = linear(),
-    relative: boolean = false
-) {
-    animateLight(id, 'x', x, time, mode, relative);
-    animateLight(id, 'y', y, time, mode, relative);
-}
-
-/**
- * 以一个路径移动光源
- * @param id 灯光id
- * @param time 移动时长
- * @param path 移动路径
- * @param mode 移动方式,渐变函数,表示移动的完成度
- * @param relative 相对模式,相对还是绝对
- */
-export function moveLightAs(
-    id: string,
-    time: number,
-    path: PathFn,
-    mode: TimingFn = linear(),
-    relative: boolean = true
-) {
-    const light = getLight(id);
-    if (!has(light)) {
-        throw new ReferenceError(`You are going to animate nonexistent light`);
-    }
-    const ani = animationList[id] ?? (animationList[id] = new Animation());
-    ani.mode(linear()).time(0).move(light.x, light.y);
-    ani.time(time)
-        .mode(mode)
-        [relative ? 'relative' : 'absolute']()
-        .moveAs(path);
-    const start = Date.now();
-    const fn = () => {
-        if (Date.now() - start > time + 50) {
-            ani.ticker.remove(fn);
-            light._animating!.x = false;
-            light._animating!.y = false;
-        }
-        needRefresh = true;
-        light.x = ani.x;
-        light.y = ani.y;
-    };
-    ani.ticker.add(fn);
-    light._animating ??= {};
-    light._animating.x = true;
-    light._animating.y = true;
-}
-
-export function animateLightColor(
-    id: string,
-    target: Color,
-    time: number = 1000,
-    mode: TimingFn = linear()
-) {
-    // todo
-}
-
-/**
- * 根据坐标数组设置物体节点
- * @param nodes 坐标数组
- */
-export function setShadowNodes(nodes: LocArr[][]): void;
-/**
- * 根据多边形数组设置物体节点
- * @param nodes 多边形数组
- */
-export function setShadowNodes(nodes: Polygon[]): void;
-export function setShadowNodes(nodes: LocArr[][] | Polygon[]) {
-    if (nodes.length === 0) {
-        shadowNodes = [];
-        needRefresh = true;
-    }
-    if (nodes[0] instanceof Polygon) shadowNodes = nodes as Polygon[];
-    else shadowNodes = Polygon.from(...(nodes as LocArr[][]));
-    needRefresh = true;
-}
-
-/**
- * 根据坐标数组添加物体节点
- * @param polygons 坐标数组
- */
-export function addPolygon(...polygons: LocArr[][]): void;
-/**
- * 根据多边形数组添加物体节点
- * @param polygons 多边形数组
- */
-export function addPolygon(...polygons: Polygon[]): void;
-export function addPolygon(...polygons: Polygon[] | LocArr[][]) {
-    if (polygons.length === 0) return;
-    if (polygons[0] instanceof Polygon)
-        shadowNodes.push(...(polygons as Polygon[]));
-    else shadowNodes.push(...Polygon.from(...(polygons as LocArr[][])));
-    needRefresh = true;
-}
-
-/**
- * 设置光源的虚化程度
- * @param n 虚化程度
- */
-export function setBlur(n: number) {
-    blur = n;
-    canvas.style.filter = `blur(${n}px)`;
-}
-
-/**
- * 绘制阴影
- */
-export function drawShadow() {
-    const w = (core._PX_ ?? core.__PIXELS__) + 64;
-    const h = (core._PY_ ?? core.__PIXELS__) + 64;
-    needRefresh = false;
-    ctx.clearRect(0, 0, w, h);
-    ct1.clearRect(0, 0, w, h);
-    ct2.clearRect(0, 0, w, h);
-    ct3.clearRect(0, 0, w, h);
-
-    const b = core.arrayToRGBA(background);
-    ctx.globalCompositeOperation = 'source-over';
-    ct3.globalCompositeOperation = 'source-over';
-
-    // 绘制阴影,一个光源一个光源地绘制,然后source-out获得光,然后把光叠加,再source-out获得最终阴影
-    for (let i = 0; i < lights.length; i++) {
-        const { x, y, r, decay, color, noShelter } = lights[i];
-        const rx = x + 32;
-        const ry = y + 32;
-        // 绘制阴影
-        ct1.clearRect(0, 0, w, h);
-        ct2.clearRect(0, 0, w, h);
-        if (!noShelter) {
-            for (const polygon of shadowNodes) {
-                const area = polygon.shadowArea(rx, ry, r);
-                area.forEach(v => {
-                    ct1.beginPath();
-                    ct1.moveTo(v[0][0], v[0][1]);
-                    for (let i = 1; i < v.length; i++) {
-                        ct1.lineTo(v[i][0], v[i][1]);
-                    }
-                    ct1.closePath();
-                    ct1.fillStyle = '#000';
-                    ct1.globalCompositeOperation = 'source-over';
-                    ct1.fill();
-                });
-            }
-        }
-        // 存入ct2,用于绘制真实阴影
-        ct2.globalCompositeOperation = 'source-over';
-        ct2.drawImage(temp1, 0, 0, w, h);
-        ct2.globalCompositeOperation = 'source-out';
-        const gra = ct2.createRadialGradient(rx, ry, decay, rx, ry, r);
-        gra.addColorStop(0, core.arrayToRGBA(color));
-        gra.addColorStop(1, 'transparent');
-        ct2.fillStyle = gra;
-        ct2.beginPath();
-        ct2.arc(rx, ry, r, 0, Math.PI * 2);
-        ct2.fill();
-        ctx.drawImage(temp2, 0, 0, w, h);
-        // 再绘制ct1的阴影,然后绘制到ct3叠加
-        ct1.globalCompositeOperation = 'source-out';
-        const gra2 = ct1.createRadialGradient(rx, ry, decay, rx, ry, r);
-        gra2.addColorStop(0, '#fff');
-        gra2.addColorStop(1, '#fff0');
-        ct1.beginPath();
-        ct1.arc(rx, ry, r, 0, Math.PI * 2);
-        ct1.fillStyle = gra2;
-        ct1.fill();
-        // 绘制到ct3上
-        ct3.drawImage(temp1, 0, 0, w, h);
-    }
-    // 绘制真实阴影
-    ct3.globalCompositeOperation = 'source-out';
-    ct3.fillStyle = b;
-    ct3.fillRect(0, 0, w, h);
-    ctx.globalCompositeOperation = 'destination-over';
-    ctx.drawImage(temp3, 0, 0, w, h);
-}