mirror of
				https://github.com/unanmed/HumanBreak.git
				synced 2025-11-04 07:02:58 +08:00 
			
		
		
		
	feat: 分离小地图绘制
This commit is contained in:
		
							parent
							
								
									1279dc9f8f
								
							
						
					
					
						commit
						0fa07e0605
					
				
							
								
								
									
										2
									
								
								idea.md
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								idea.md
									
									
									
									
									
								
							@ -97,7 +97,7 @@ dam4.png ---- 存档 59
 | 
			
		||||
[] 技能树允许自动升级
 | 
			
		||||
[] 重构装备系统
 | 
			
		||||
[] 弹幕系统
 | 
			
		||||
[] 优化各种 ui
 | 
			
		||||
[x] 优化各种 ui
 | 
			
		||||
[] 怪物脚下加入阴影
 | 
			
		||||
[x] 着色器特效
 | 
			
		||||
[x] 完全删除 core.plugin,采用 Plugin.register 的形式进行插件编写
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ main.floors.MT50=
 | 
			
		||||
            "左边两个机关门在打完左下角区域的boss后开启,右边同理。"
 | 
			
		||||
        ],
 | 
			
		||||
        "9,1": [
 | 
			
		||||
            "左下角和右下角两个区域打完之后必须要点开学习技能,不然会卡关。",
 | 
			
		||||
            "建议优先点出学习技能,对于特定场景将会非常有帮助",
 | 
			
		||||
            "本区域可以使用跳跃技能,不要忘记了。"
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -33,21 +33,17 @@ class GameLoading extends EventEmitter<GameLoadEvent> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addMaterialLoaded() {
 | 
			
		||||
        this.once('coreInit', () => {
 | 
			
		||||
            this.materialsLoaded++;
 | 
			
		||||
            if (this.materialsLoaded === this.materialsNum) {
 | 
			
		||||
                this.emit('materialLoaded');
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        this.materialsLoaded++;
 | 
			
		||||
        if (this.materialsLoaded === this.materialsNum) {
 | 
			
		||||
            this.emit('materialLoaded');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addAutotileLoaded() {
 | 
			
		||||
        this.once('coreInit', () => {
 | 
			
		||||
            this.autotileLoaded++;
 | 
			
		||||
            if (this.autotileLoaded === this.autotileNum) {
 | 
			
		||||
                this.emit('autotileLoaded');
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        this.autotileLoaded++;
 | 
			
		||||
        if (this.autotileLoaded === this.autotileNum) {
 | 
			
		||||
            this.emit('autotileLoaded');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,5 @@
 | 
			
		||||
import { hook } from '@/game/game';
 | 
			
		||||
 | 
			
		||||
export {};
 | 
			
		||||
 | 
			
		||||
const potionItems: AllIdsOf<'items'>[] = [
 | 
			
		||||
@ -11,8 +13,6 @@ const potionItems: AllIdsOf<'items'>[] = [
 | 
			
		||||
    'I491'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const hook = Mota.require('var', 'hook');
 | 
			
		||||
 | 
			
		||||
hook.on('afterGetItem', (itemId, x, y, isGentleClick) => {
 | 
			
		||||
    // 获得一个道具后触发的事件
 | 
			
		||||
    // itemId:获得的道具ID;x和y是该道具所在的坐标
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import * as utils from './utils';
 | 
			
		||||
import * as chase from './chase';
 | 
			
		||||
import * as remainEnemy from './enemy/remainEnemy';
 | 
			
		||||
import * as checkBlock from './enemy/checkblock';
 | 
			
		||||
import './hook';
 | 
			
		||||
 | 
			
		||||
Mota.Plugin.register('utils_g', utils);
 | 
			
		||||
Mota.Plugin.register('loopMap_g', loopMap, loopMap.init);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { has } from '../utils';
 | 
			
		||||
import { downloadCanvasImage, has, tip } from '../utils';
 | 
			
		||||
 | 
			
		||||
type BFSFromString = `${FloorIds},${number},${number},${Dir}`;
 | 
			
		||||
type BFSToString = `${FloorIds},${number},${number}`;
 | 
			
		||||
@ -200,3 +200,294 @@ export function getMapData(
 | 
			
		||||
 | 
			
		||||
    return (bfsCache[floorId] = res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Loc2 = [number, number, number, number];
 | 
			
		||||
 | 
			
		||||
export class MinimapDrawer {
 | 
			
		||||
    ctx: CanvasRenderingContext2D;
 | 
			
		||||
    canvas: HTMLCanvasElement;
 | 
			
		||||
    scale: number = 1;
 | 
			
		||||
    nowFloor: FloorIds = core.status.floorId;
 | 
			
		||||
    nowArea: string = '';
 | 
			
		||||
 | 
			
		||||
    // position & config
 | 
			
		||||
    ox: number = 0;
 | 
			
		||||
    oy: number = 0;
 | 
			
		||||
    noBorder: boolean = false;
 | 
			
		||||
 | 
			
		||||
    // cache
 | 
			
		||||
    drawedThumbnail: Partial<Record<FloorIds, boolean>> = {};
 | 
			
		||||
    thumbnailLoc: Partial<Record<FloorIds, Loc2>> = {};
 | 
			
		||||
 | 
			
		||||
    // temp
 | 
			
		||||
    private tempCanvas: HTMLCanvasElement = document.createElement('canvas');
 | 
			
		||||
    private tempCtx: CanvasRenderingContext2D;
 | 
			
		||||
 | 
			
		||||
    private downloadMode: boolean = false;
 | 
			
		||||
 | 
			
		||||
    constructor(canvas: HTMLCanvasElement) {
 | 
			
		||||
        this.canvas = canvas;
 | 
			
		||||
        this.ctx = canvas.getContext('2d')!;
 | 
			
		||||
        this.tempCtx = this.tempCanvas.getContext('2d')!;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    link(canvas: HTMLCanvasElement) {
 | 
			
		||||
        this.canvas = canvas;
 | 
			
		||||
        this.ctx = canvas.getContext('2d')!;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    clearCache() {
 | 
			
		||||
        this.drawedThumbnail = {};
 | 
			
		||||
        this.thumbnailLoc = {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 绘制小地图
 | 
			
		||||
     * @param noCache 是否不使用缓存
 | 
			
		||||
     */
 | 
			
		||||
    drawMap(noCache: boolean = false) {
 | 
			
		||||
        const border = this.noBorder ? 0.5 : 1;
 | 
			
		||||
        const data = getMapDrawData(
 | 
			
		||||
            this.nowFloor,
 | 
			
		||||
            this.noBorder ? 0 : 5,
 | 
			
		||||
            border,
 | 
			
		||||
            noCache
 | 
			
		||||
        );
 | 
			
		||||
        const temp = this.tempCanvas;
 | 
			
		||||
        const ctx = this.tempCtx;
 | 
			
		||||
        const s = this.scale * devicePixelRatio;
 | 
			
		||||
        temp.width = data.width * s;
 | 
			
		||||
        temp.height = data.height * s;
 | 
			
		||||
        ctx.lineWidth = (border * devicePixelRatio) / 2;
 | 
			
		||||
        ctx.strokeStyle = '#fff';
 | 
			
		||||
        ctx.scale(s, s);
 | 
			
		||||
        ctx.translate(5, 5);
 | 
			
		||||
 | 
			
		||||
        if (!this.noBorder) {
 | 
			
		||||
            // 绘制连线
 | 
			
		||||
            data.line.forEach(([x1, y1, x2, y2]) => {
 | 
			
		||||
                ctx.beginPath();
 | 
			
		||||
                ctx.moveTo(x1, y1);
 | 
			
		||||
                ctx.lineTo(x2, y2);
 | 
			
		||||
                ctx.stroke();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 绘制地图及缩略图
 | 
			
		||||
        for (const [id, [x, y]] of Object.entries(data.locs) as [
 | 
			
		||||
            FloorIds,
 | 
			
		||||
            LocArr
 | 
			
		||||
        ][]) {
 | 
			
		||||
            if (!this.noBorder) this.drawBorder(id, x, y);
 | 
			
		||||
            this.drawThumbnail(id, x, y);
 | 
			
		||||
        }
 | 
			
		||||
        this.drawToTarget();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 绘制一个楼层的边框
 | 
			
		||||
     */
 | 
			
		||||
    drawBorder(id: FloorIds, x: number, y: number) {
 | 
			
		||||
        const border = this.noBorder ? 0.5 : 1;
 | 
			
		||||
        const ctx = this.tempCtx;
 | 
			
		||||
        ctx.lineWidth = border * devicePixelRatio;
 | 
			
		||||
        const map = core.status.maps[id];
 | 
			
		||||
 | 
			
		||||
        if (!core.hasVisitedFloor(id)) {
 | 
			
		||||
            ctx.fillStyle = '#d0d';
 | 
			
		||||
        } else {
 | 
			
		||||
            ctx.fillStyle = '#000';
 | 
			
		||||
        }
 | 
			
		||||
        if (id === this.nowFloor) {
 | 
			
		||||
            ctx.strokeStyle = 'gold';
 | 
			
		||||
        } else {
 | 
			
		||||
            ctx.strokeStyle = '#fff';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ctx.strokeRect(
 | 
			
		||||
            x - map.width / 2,
 | 
			
		||||
            y - map.height / 2,
 | 
			
		||||
            map.width,
 | 
			
		||||
            map.height
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        ctx.fillRect(
 | 
			
		||||
            x - map.width / 2,
 | 
			
		||||
            y - map.height / 2,
 | 
			
		||||
            map.width,
 | 
			
		||||
            map.height
 | 
			
		||||
        );
 | 
			
		||||
        if (id === this.nowFloor) {
 | 
			
		||||
            ctx.fillStyle = '#ff04';
 | 
			
		||||
            ctx.fillRect(
 | 
			
		||||
                x - map.width / 2,
 | 
			
		||||
                y - map.height / 2,
 | 
			
		||||
                map.width,
 | 
			
		||||
                map.height
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将临时画布的内容绘制到目标画布上
 | 
			
		||||
     */
 | 
			
		||||
    drawToTarget() {
 | 
			
		||||
        const mapCtx = this.ctx;
 | 
			
		||||
        const map = this.canvas;
 | 
			
		||||
        const temp = this.tempCanvas;
 | 
			
		||||
 | 
			
		||||
        mapCtx.clearRect(0, 0, map.width, map.height);
 | 
			
		||||
        mapCtx.drawImage(
 | 
			
		||||
            temp,
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            temp.width,
 | 
			
		||||
            temp.height,
 | 
			
		||||
            this.ox * devicePixelRatio + (map.width - temp.width) / 2,
 | 
			
		||||
            this.oy * devicePixelRatio + (map.height - temp.height) / 2,
 | 
			
		||||
            temp.width,
 | 
			
		||||
            temp.height
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查是否应该绘制缩略图
 | 
			
		||||
     */
 | 
			
		||||
    checkThumbnail(floorId: FloorIds, x: number, y: number) {
 | 
			
		||||
        const scale = this.scale;
 | 
			
		||||
        const ox = this.ox;
 | 
			
		||||
        const oy = this.oy;
 | 
			
		||||
        const map = this.canvas;
 | 
			
		||||
        const temp = this.tempCanvas;
 | 
			
		||||
 | 
			
		||||
        const floor = core.status.maps[floorId];
 | 
			
		||||
        const s = scale * devicePixelRatio;
 | 
			
		||||
        const px = ox * devicePixelRatio + (map.width - temp.width) / 2 + 5 * s;
 | 
			
		||||
        const py =
 | 
			
		||||
            oy * devicePixelRatio + (map.height - temp.height) / 2 + 5 * s;
 | 
			
		||||
        const left = px + (x - floor.width / 2) * s;
 | 
			
		||||
        const top = py + (y - floor.height / 2) * s;
 | 
			
		||||
        const right = left + floor.width * s;
 | 
			
		||||
        const bottom = top + floor.height * s;
 | 
			
		||||
 | 
			
		||||
        this.thumbnailLoc[floorId] = [left, top, right, bottom];
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            this.drawedThumbnail[floorId] ||
 | 
			
		||||
            (!this.noBorder && scale <= 4) ||
 | 
			
		||||
            right < 0 ||
 | 
			
		||||
            bottom < 0 ||
 | 
			
		||||
            left > map.width ||
 | 
			
		||||
            top > map.height
 | 
			
		||||
        )
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 绘制缩略图
 | 
			
		||||
     */
 | 
			
		||||
    drawThumbnail(
 | 
			
		||||
        floorId: FloorIds,
 | 
			
		||||
        x: number,
 | 
			
		||||
        y: number,
 | 
			
		||||
        noCheck: boolean = false
 | 
			
		||||
    ) {
 | 
			
		||||
        if (
 | 
			
		||||
            !this.downloadMode &&
 | 
			
		||||
            !noCheck &&
 | 
			
		||||
            !this.checkThumbnail(floorId, x, y)
 | 
			
		||||
        )
 | 
			
		||||
            return;
 | 
			
		||||
        const floor = core.status.maps[floorId];
 | 
			
		||||
        this.drawedThumbnail[floorId] = true;
 | 
			
		||||
 | 
			
		||||
        // 绘制缩略图
 | 
			
		||||
        const ctx = this.tempCtx;
 | 
			
		||||
        core.drawThumbnail(floorId, void 0, {
 | 
			
		||||
            all: true,
 | 
			
		||||
            inFlyMap: true,
 | 
			
		||||
            x: x - floor.width / 2,
 | 
			
		||||
            y: y - floor.height / 2,
 | 
			
		||||
            w: floor.width,
 | 
			
		||||
            h: floor.height,
 | 
			
		||||
            ctx,
 | 
			
		||||
            damage: this.scale > 7
 | 
			
		||||
        });
 | 
			
		||||
        if (!this.downloadMode) {
 | 
			
		||||
            if (!core.hasVisitedFloor(floorId)) {
 | 
			
		||||
                ctx.fillStyle = '#d0d6';
 | 
			
		||||
                ctx.fillRect(
 | 
			
		||||
                    x - floor.width / 2,
 | 
			
		||||
                    y - floor.height / 2,
 | 
			
		||||
                    floor.width,
 | 
			
		||||
                    floor.height
 | 
			
		||||
                );
 | 
			
		||||
                ctx.fillStyle = '#000';
 | 
			
		||||
            }
 | 
			
		||||
            if (this.nowFloor === floorId) {
 | 
			
		||||
                ctx.fillStyle = '#ff04';
 | 
			
		||||
                ctx.fillRect(
 | 
			
		||||
                    x - floor.width / 2,
 | 
			
		||||
                    y - floor.height / 2,
 | 
			
		||||
                    floor.width,
 | 
			
		||||
                    floor.height
 | 
			
		||||
                );
 | 
			
		||||
                ctx.fillStyle = '#000';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 当移动时检查是否应该绘制缩略图
 | 
			
		||||
     */
 | 
			
		||||
    checkMoveThumbnail() {
 | 
			
		||||
        const border = this.noBorder ? 0.5 : 1;
 | 
			
		||||
        const data = getMapDrawData(
 | 
			
		||||
            this.nowFloor,
 | 
			
		||||
            this.noBorder ? 0 : 5,
 | 
			
		||||
            border
 | 
			
		||||
        );
 | 
			
		||||
        for (const [id, [x, y]] of Object.entries(data.locs) as [
 | 
			
		||||
            FloorIds,
 | 
			
		||||
            LocArr
 | 
			
		||||
        ][]) {
 | 
			
		||||
            if (this.checkThumbnail(id, x, y)) {
 | 
			
		||||
                this.drawThumbnail(id, x, y, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    download() {
 | 
			
		||||
        if (this.nowArea === '') {
 | 
			
		||||
            tip('error', '当前地图不在任意一个区域内!');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.downloadMode = true;
 | 
			
		||||
        const before = this.scale;
 | 
			
		||||
        this.scale = 32;
 | 
			
		||||
        this.drawMap();
 | 
			
		||||
        downloadCanvasImage(this.tempCanvas, this.nowArea);
 | 
			
		||||
        this.scale = before;
 | 
			
		||||
        this.downloadMode = false;
 | 
			
		||||
        tip('success', '图片下载成功!');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 居中地图
 | 
			
		||||
     * @param id 楼层id
 | 
			
		||||
     */
 | 
			
		||||
    locateMap(id: FloorIds) {
 | 
			
		||||
        const data = getMapDrawData(
 | 
			
		||||
            id,
 | 
			
		||||
            this.noBorder ? 0 : 5, // 可恶的0和5,写反了找一个多小时
 | 
			
		||||
            this.noBorder ? 0.5 : 1
 | 
			
		||||
        );
 | 
			
		||||
        if (!data.locs[id]) return;
 | 
			
		||||
 | 
			
		||||
        const [x, y] = data.locs[id]!;
 | 
			
		||||
        this.ox = (-x + data.width / 2 - 5) * this.scale;
 | 
			
		||||
        this.oy = (-y + data.height / 2 - 5) * this.scale;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										285
									
								
								src/ui/fly.vue
									
									
									
									
									
								
							
							
						
						
									
										285
									
								
								src/ui/fly.vue
									
									
									
									
									
								
							@ -89,7 +89,12 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
 | 
			
		||||
import Scroll from '../components/scroll.vue';
 | 
			
		||||
import { getArea, getMapDrawData, getMapData } from '../plugin/ui/fly';
 | 
			
		||||
import {
 | 
			
		||||
    getArea,
 | 
			
		||||
    getMapDrawData,
 | 
			
		||||
    getMapData,
 | 
			
		||||
    MinimapDrawer
 | 
			
		||||
} from '../plugin/ui/fly';
 | 
			
		||||
import { cancelGlobalDrag, isMobile, useDrag, useWheel } from '../plugin/use';
 | 
			
		||||
import {
 | 
			
		||||
    LeftOutlined,
 | 
			
		||||
@ -121,10 +126,6 @@ const noBorder = ref(true);
 | 
			
		||||
const tradition = ref(false);
 | 
			
		||||
let scale =
 | 
			
		||||
    ((isMobile ? 1.5 : 3) * mainSetting.getValue('ui.mapScale', 100)) / 100;
 | 
			
		||||
let ox = 0;
 | 
			
		||||
let oy = 0;
 | 
			
		||||
let drawedThumbnail: Partial<Record<FloorIds, boolean>> = {};
 | 
			
		||||
let thumbnailLoc: Partial<Record<FloorIds, Loc2>> = {};
 | 
			
		||||
 | 
			
		||||
noBorder.value = core.getLocalStorage('noBorder', true);
 | 
			
		||||
tradition.value = core.getLocalStorage('flyTradition', false);
 | 
			
		||||
@ -133,32 +134,34 @@ const floor = computed(() => {
 | 
			
		||||
    return core.status.maps[nowFloor.value];
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watch(nowFloor, draw);
 | 
			
		||||
watch(nowFloor, n => {
 | 
			
		||||
    drawer.nowFloor = n;
 | 
			
		||||
    draw();
 | 
			
		||||
});
 | 
			
		||||
watch(nowArea, n => {
 | 
			
		||||
    ox = 0;
 | 
			
		||||
    oy = 0;
 | 
			
		||||
    scale = 3;
 | 
			
		||||
    lastScale = 3;
 | 
			
		||||
    drawer.nowArea = n;
 | 
			
		||||
    drawer.scale = 3;
 | 
			
		||||
    drawer.ox = 0;
 | 
			
		||||
    drawer.oy = 0;
 | 
			
		||||
    if (area[n] && !area[n].includes(nowFloor.value))
 | 
			
		||||
        nowFloor.value =
 | 
			
		||||
            area[n].find(v => v === core.status.floorId) ?? area[n][0];
 | 
			
		||||
});
 | 
			
		||||
watch(noBorder, n => {
 | 
			
		||||
    core.setLocalStorage('noBorder', n);
 | 
			
		||||
    drawedThumbnail = {};
 | 
			
		||||
    drawMap();
 | 
			
		||||
    drawer.noBorder = true;
 | 
			
		||||
    drawer.drawedThumbnail = {};
 | 
			
		||||
    drawer.drawMap();
 | 
			
		||||
});
 | 
			
		||||
watch(tradition, n => {
 | 
			
		||||
    core.setLocalStorage('flyTradition', n);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const temp = document.createElement('canvas');
 | 
			
		||||
const tempCtx = temp.getContext('2d')!;
 | 
			
		||||
let map: HTMLCanvasElement;
 | 
			
		||||
let mapCtx: CanvasRenderingContext2D;
 | 
			
		||||
let thumb: HTMLCanvasElement;
 | 
			
		||||
let thumbCtx: CanvasRenderingContext2D;
 | 
			
		||||
let downloadMode = false;
 | 
			
		||||
 | 
			
		||||
function exit() {
 | 
			
		||||
    mainUi.close(props.num);
 | 
			
		||||
@ -169,192 +172,7 @@ const title = computed(() => {
 | 
			
		||||
});
 | 
			
		||||
const titleChange = createChangable(title).change;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 绘制小地图
 | 
			
		||||
 * @param noCache 是否不使用缓存
 | 
			
		||||
 */
 | 
			
		||||
function drawMap(noCache: boolean = false) {
 | 
			
		||||
    const border = noBorder.value ? 0.5 : 1;
 | 
			
		||||
    const data = getMapDrawData(
 | 
			
		||||
        nowFloor.value,
 | 
			
		||||
        noBorder.value ? 0 : 5,
 | 
			
		||||
        border,
 | 
			
		||||
        noCache
 | 
			
		||||
    );
 | 
			
		||||
    const ctx = tempCtx;
 | 
			
		||||
    const s = scale * devicePixelRatio;
 | 
			
		||||
    temp.width = data.width * s;
 | 
			
		||||
    temp.height = data.height * s;
 | 
			
		||||
    ctx.lineWidth = (border * devicePixelRatio) / 2;
 | 
			
		||||
    ctx.strokeStyle = '#fff';
 | 
			
		||||
    ctx.scale(s, s);
 | 
			
		||||
    ctx.translate(5, 5);
 | 
			
		||||
 | 
			
		||||
    if (!noBorder.value) {
 | 
			
		||||
        // 绘制连线
 | 
			
		||||
        data.line.forEach(([x1, y1, x2, y2]) => {
 | 
			
		||||
            ctx.beginPath();
 | 
			
		||||
            ctx.moveTo(x1, y1);
 | 
			
		||||
            ctx.lineTo(x2, y2);
 | 
			
		||||
            ctx.stroke();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 绘制地图及缩略图
 | 
			
		||||
    for (const [id, [x, y]] of Object.entries(data.locs) as [
 | 
			
		||||
        FloorIds,
 | 
			
		||||
        LocArr
 | 
			
		||||
    ][]) {
 | 
			
		||||
        if (!noBorder.value) drawBorder(id, x, y);
 | 
			
		||||
        drawThumbnail(id, x, y);
 | 
			
		||||
    }
 | 
			
		||||
    drawToTarget();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function drawBorder(id: FloorIds, x: number, y: number) {
 | 
			
		||||
    const border = noBorder.value ? 0.5 : 1;
 | 
			
		||||
    const ctx = tempCtx;
 | 
			
		||||
    ctx.lineWidth = border * devicePixelRatio;
 | 
			
		||||
    const map = core.status.maps[id];
 | 
			
		||||
 | 
			
		||||
    if (!core.hasVisitedFloor(id)) {
 | 
			
		||||
        ctx.fillStyle = '#d0d';
 | 
			
		||||
    } else {
 | 
			
		||||
        ctx.fillStyle = '#000';
 | 
			
		||||
    }
 | 
			
		||||
    if (id === nowFloor.value) {
 | 
			
		||||
        ctx.strokeStyle = 'gold';
 | 
			
		||||
    } else {
 | 
			
		||||
        ctx.strokeStyle = '#fff';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ctx.strokeRect(
 | 
			
		||||
        x - map.width / 2,
 | 
			
		||||
        y - map.height / 2,
 | 
			
		||||
        map.width,
 | 
			
		||||
        map.height
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    ctx.fillRect(x - map.width / 2, y - map.height / 2, map.width, map.height);
 | 
			
		||||
    if (id === nowFloor.value) {
 | 
			
		||||
        ctx.fillStyle = '#ff04';
 | 
			
		||||
        ctx.fillRect(
 | 
			
		||||
            x - map.width / 2,
 | 
			
		||||
            y - map.height / 2,
 | 
			
		||||
            map.width,
 | 
			
		||||
            map.height
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 绘制小地图至目标画布
 | 
			
		||||
 */
 | 
			
		||||
function drawToTarget(s: number = 1) {
 | 
			
		||||
    mapCtx.clearRect(0, 0, map.width, map.height);
 | 
			
		||||
    mapCtx.drawImage(
 | 
			
		||||
        temp,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        temp.width,
 | 
			
		||||
        temp.height,
 | 
			
		||||
        ox * devicePixelRatio + (map.width - temp.width) / 2,
 | 
			
		||||
        oy * devicePixelRatio + (map.height - temp.height) / 2,
 | 
			
		||||
        temp.width,
 | 
			
		||||
        temp.height
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 检查是否应该绘制缩略图
 | 
			
		||||
 */
 | 
			
		||||
function checkThumbnail(floorId: FloorIds, x: number, y: number) {
 | 
			
		||||
    const floor = core.status.maps[floorId];
 | 
			
		||||
    const s = scale * devicePixelRatio;
 | 
			
		||||
    const px = ox * devicePixelRatio + (map.width - temp.width) / 2 + 5 * s;
 | 
			
		||||
    const py = oy * devicePixelRatio + (map.height - temp.height) / 2 + 5 * s;
 | 
			
		||||
    const left = px + (x - floor.width / 2) * s;
 | 
			
		||||
    const top = py + (y - floor.height / 2) * s;
 | 
			
		||||
    const right = left + floor.width * s;
 | 
			
		||||
    const bottom = top + floor.height * s;
 | 
			
		||||
 | 
			
		||||
    thumbnailLoc[floorId] = [left, top, right, bottom];
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
        drawedThumbnail[floorId] ||
 | 
			
		||||
        (!noBorder.value && scale <= 4) ||
 | 
			
		||||
        right < 0 ||
 | 
			
		||||
        bottom < 0 ||
 | 
			
		||||
        left > map.width ||
 | 
			
		||||
        top > map.height
 | 
			
		||||
    )
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 绘制缩略图
 | 
			
		||||
 */
 | 
			
		||||
function drawThumbnail(
 | 
			
		||||
    floorId: FloorIds,
 | 
			
		||||
    x: number,
 | 
			
		||||
    y: number,
 | 
			
		||||
    noCheck: boolean = false
 | 
			
		||||
) {
 | 
			
		||||
    if (!downloadMode && !noCheck && !checkThumbnail(floorId, x, y)) return;
 | 
			
		||||
    const floor = core.status.maps[floorId];
 | 
			
		||||
    drawedThumbnail[floorId] = true;
 | 
			
		||||
 | 
			
		||||
    // 绘制缩略图
 | 
			
		||||
    const ctx = tempCtx;
 | 
			
		||||
    core.drawThumbnail(floorId, void 0, {
 | 
			
		||||
        all: true,
 | 
			
		||||
        inFlyMap: true,
 | 
			
		||||
        x: x - floor.width / 2,
 | 
			
		||||
        y: y - floor.height / 2,
 | 
			
		||||
        w: floor.width,
 | 
			
		||||
        h: floor.height,
 | 
			
		||||
        ctx,
 | 
			
		||||
        damage: scale > 7
 | 
			
		||||
    });
 | 
			
		||||
    if (!downloadMode) {
 | 
			
		||||
        if (!core.hasVisitedFloor(floorId)) {
 | 
			
		||||
            ctx.fillStyle = '#d0d6';
 | 
			
		||||
            ctx.fillRect(
 | 
			
		||||
                x - floor.width / 2,
 | 
			
		||||
                y - floor.height / 2,
 | 
			
		||||
                floor.width,
 | 
			
		||||
                floor.height
 | 
			
		||||
            );
 | 
			
		||||
            ctx.fillStyle = '#000';
 | 
			
		||||
        }
 | 
			
		||||
        if (nowFloor.value === floorId) {
 | 
			
		||||
            ctx.fillStyle = '#ff04';
 | 
			
		||||
            ctx.fillRect(
 | 
			
		||||
                x - floor.width / 2,
 | 
			
		||||
                y - floor.height / 2,
 | 
			
		||||
                floor.width,
 | 
			
		||||
                floor.height
 | 
			
		||||
            );
 | 
			
		||||
            ctx.fillStyle = '#000';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 当移动时检查是否应该绘制缩略图
 | 
			
		||||
 */
 | 
			
		||||
function checkMoveThumbnail() {
 | 
			
		||||
    const border = noBorder.value ? 0.5 : 1;
 | 
			
		||||
    const data = getMapDrawData(nowFloor.value, noBorder.value ? 0 : 5, border);
 | 
			
		||||
    for (const [id, [x, y]] of Object.entries(data.locs) as [
 | 
			
		||||
        FloorIds,
 | 
			
		||||
        LocArr
 | 
			
		||||
    ][]) {
 | 
			
		||||
        if (checkThumbnail(id, x, y)) drawThumbnail(id, x, y, true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
let drawer: MinimapDrawer;
 | 
			
		||||
 | 
			
		||||
function drawRight() {
 | 
			
		||||
    let w = thumb.width;
 | 
			
		||||
@ -388,26 +206,14 @@ function drawRight() {
 | 
			
		||||
 * 绘制所有内容
 | 
			
		||||
 */
 | 
			
		||||
function draw() {
 | 
			
		||||
    drawedThumbnail = {};
 | 
			
		||||
    thumbnailLoc = {};
 | 
			
		||||
    drawMap();
 | 
			
		||||
    drawer.clearCache();
 | 
			
		||||
    drawer.drawMap();
 | 
			
		||||
    drawRight();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function download() {
 | 
			
		||||
    if (nowArea.value === '') {
 | 
			
		||||
        tip('error', '当前地图不在任意一个区域内!');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    downloadMode = true;
 | 
			
		||||
    const before = scale;
 | 
			
		||||
    scale = 32;
 | 
			
		||||
    drawMap();
 | 
			
		||||
    downloadCanvasImage(temp, nowArea.value);
 | 
			
		||||
    scale = before;
 | 
			
		||||
    downloadMode = false;
 | 
			
		||||
    drawer.download();
 | 
			
		||||
    draw();
 | 
			
		||||
    tip('success', '图片下载成功!');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function fly() {
 | 
			
		||||
@ -418,18 +224,19 @@ function fly() {
 | 
			
		||||
let lastScale = scale;
 | 
			
		||||
const changeScale = debounce((s: number) => {
 | 
			
		||||
    map.style.transform = '';
 | 
			
		||||
    drawedThumbnail = {};
 | 
			
		||||
    drawMap();
 | 
			
		||||
    drawer.drawedThumbnail = {};
 | 
			
		||||
    drawer.scale = s;
 | 
			
		||||
    drawer.drawMap();
 | 
			
		||||
    lastScale = s;
 | 
			
		||||
}, 200);
 | 
			
		||||
 | 
			
		||||
function resize(delta: number) {
 | 
			
		||||
    ox *= delta;
 | 
			
		||||
    oy *= delta;
 | 
			
		||||
    drawer.ox *= delta;
 | 
			
		||||
    drawer.oy *= delta;
 | 
			
		||||
    scale = delta * scale;
 | 
			
		||||
    changeScale(scale);
 | 
			
		||||
    map.style.transform = `scale(${scale / lastScale})`;
 | 
			
		||||
    thumbnailLoc = {};
 | 
			
		||||
    drawer.thumbnailLoc = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let lastX = 0;
 | 
			
		||||
@ -445,12 +252,12 @@ function drag(x: number, y: number) {
 | 
			
		||||
    if (touchScale) return;
 | 
			
		||||
    const dx = x - lastX;
 | 
			
		||||
    const dy = y - lastY;
 | 
			
		||||
    ox += dx;
 | 
			
		||||
    oy += dy;
 | 
			
		||||
    drawer.ox += dx;
 | 
			
		||||
    drawer.oy += dy;
 | 
			
		||||
    lastX = x;
 | 
			
		||||
    lastY = y;
 | 
			
		||||
    checkMoveThumbnail();
 | 
			
		||||
    drawToTarget();
 | 
			
		||||
    drawer.checkMoveThumbnail();
 | 
			
		||||
    drawer.drawToTarget();
 | 
			
		||||
    if (Math.abs(x - startX) > 10 || Math.abs(y - startY) > 10) moved = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -460,7 +267,7 @@ function click(e: MouseEvent) {
 | 
			
		||||
    const x = e.offsetX * devicePixelRatio;
 | 
			
		||||
    const y = e.offsetY * devicePixelRatio;
 | 
			
		||||
    for (const [id, [left, top, right, bottom]] of Object.entries(
 | 
			
		||||
        thumbnailLoc
 | 
			
		||||
        drawer.thumbnailLoc
 | 
			
		||||
    ) as [FloorIds, Loc2][]) {
 | 
			
		||||
        if (x >= left && x <= right && y >= top && y <= bottom) {
 | 
			
		||||
            if (id === nowFloor.value) {
 | 
			
		||||
@ -497,7 +304,7 @@ function changeFloorByDelta(delta: number) {
 | 
			
		||||
    }
 | 
			
		||||
    nowFloor.value = core.floorIds[to];
 | 
			
		||||
    changeAreaByFloor(nowFloor.value);
 | 
			
		||||
    locateMap(nowFloor.value);
 | 
			
		||||
    drawer.locateMap(nowFloor.value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function changeFloorByDir(dir: Dir) {
 | 
			
		||||
@ -509,30 +316,13 @@ function changeFloorByDir(dir: Dir) {
 | 
			
		||||
 | 
			
		||||
        if (d === dir) {
 | 
			
		||||
            const target = to.split(',')[0] as FloorIds;
 | 
			
		||||
            locateMap(target);
 | 
			
		||||
            drawer.locateMap(target);
 | 
			
		||||
            nowFloor.value = target;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 居中地图
 | 
			
		||||
 * @param id 楼层id
 | 
			
		||||
 */
 | 
			
		||||
function locateMap(id: FloorIds) {
 | 
			
		||||
    const data = getMapDrawData(
 | 
			
		||||
        id,
 | 
			
		||||
        noBorder.value ? 0 : 5, // 可恶的0和5,写反了找一个多小时
 | 
			
		||||
        noBorder.value ? 0.5 : 1
 | 
			
		||||
    );
 | 
			
		||||
    if (!data.locs[id]) return;
 | 
			
		||||
 | 
			
		||||
    const [x, y] = data.locs[id]!;
 | 
			
		||||
    ox = (-x + data.width / 2 - 5) * scale;
 | 
			
		||||
    oy = (-y + data.height / 2 - 5) * scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// -------------------- 键盘事件
 | 
			
		||||
 | 
			
		||||
gameKey.use(props.ui.symbol);
 | 
			
		||||
@ -607,10 +397,13 @@ function touchmove(e: TouchEvent) {
 | 
			
		||||
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
    map = document.getElementById('fly-map') as HTMLCanvasElement;
 | 
			
		||||
    mapCtx = map.getContext('2d')!;
 | 
			
		||||
    thumb = document.getElementById('fly-thumbnail') as HTMLCanvasElement;
 | 
			
		||||
    thumbCtx = thumb.getContext('2d')!;
 | 
			
		||||
 | 
			
		||||
    drawer = new MinimapDrawer(map);
 | 
			
		||||
    drawer.scale = scale;
 | 
			
		||||
    drawer.noBorder = noBorder.value;
 | 
			
		||||
 | 
			
		||||
    const mapStyle = getComputedStyle(map);
 | 
			
		||||
    const thumbStyle = getComputedStyle(thumb);
 | 
			
		||||
    map.width = parseFloat(mapStyle.width) * devicePixelRatio;
 | 
			
		||||
@ -622,7 +415,7 @@ onMounted(async () => {
 | 
			
		||||
        v.addEventListener('click', e => (v as HTMLElement).blur());
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    locateMap(nowFloor.value);
 | 
			
		||||
    drawer.locateMap(nowFloor.value);
 | 
			
		||||
    draw();
 | 
			
		||||
 | 
			
		||||
    useDrag(
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user