mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-31 23:29:27 +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] 着色器特效
|
||||||
[x] 完全删除 core.plugin,采用 Plugin.register 的形式进行插件编写
|
[x] 完全删除 core.plugin,采用 Plugin.register 的形式进行插件编写
|
||||||
|
@ -37,7 +37,7 @@ main.floors.MT50=
|
|||||||
"左边两个机关门在打完左下角区域的boss后开启,右边同理。"
|
"左边两个机关门在打完左下角区域的boss后开启,右边同理。"
|
||||||
],
|
],
|
||||||
"9,1": [
|
"9,1": [
|
||||||
"左下角和右下角两个区域打完之后必须要点开学习技能,不然会卡关。",
|
"建议优先点出学习技能,对于特定场景将会非常有帮助",
|
||||||
"本区域可以使用跳跃技能,不要忘记了。"
|
"本区域可以使用跳跃技能,不要忘记了。"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -33,21 +33,17 @@ class GameLoading extends EventEmitter<GameLoadEvent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addMaterialLoaded() {
|
addMaterialLoaded() {
|
||||||
this.once('coreInit', () => {
|
|
||||||
this.materialsLoaded++;
|
this.materialsLoaded++;
|
||||||
if (this.materialsLoaded === this.materialsNum) {
|
if (this.materialsLoaded === this.materialsNum) {
|
||||||
this.emit('materialLoaded');
|
this.emit('materialLoaded');
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addAutotileLoaded() {
|
addAutotileLoaded() {
|
||||||
this.once('coreInit', () => {
|
|
||||||
this.autotileLoaded++;
|
this.autotileLoaded++;
|
||||||
if (this.autotileLoaded === this.autotileNum) {
|
if (this.autotileLoaded === this.autotileNum) {
|
||||||
this.emit('autotileLoaded');
|
this.emit('autotileLoaded');
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { hook } from '@/game/game';
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
|
||||||
const potionItems: AllIdsOf<'items'>[] = [
|
const potionItems: AllIdsOf<'items'>[] = [
|
||||||
@ -11,8 +13,6 @@ const potionItems: AllIdsOf<'items'>[] = [
|
|||||||
'I491'
|
'I491'
|
||||||
];
|
];
|
||||||
|
|
||||||
const hook = Mota.require('var', 'hook');
|
|
||||||
|
|
||||||
hook.on('afterGetItem', (itemId, x, y, isGentleClick) => {
|
hook.on('afterGetItem', (itemId, x, y, isGentleClick) => {
|
||||||
// 获得一个道具后触发的事件
|
// 获得一个道具后触发的事件
|
||||||
// itemId:获得的道具ID;x和y是该道具所在的坐标
|
// itemId:获得的道具ID;x和y是该道具所在的坐标
|
||||||
|
@ -17,6 +17,7 @@ import * as utils from './utils';
|
|||||||
import * as chase from './chase';
|
import * as chase from './chase';
|
||||||
import * as remainEnemy from './enemy/remainEnemy';
|
import * as remainEnemy from './enemy/remainEnemy';
|
||||||
import * as checkBlock from './enemy/checkblock';
|
import * as checkBlock from './enemy/checkblock';
|
||||||
|
import './hook';
|
||||||
|
|
||||||
Mota.Plugin.register('utils_g', utils);
|
Mota.Plugin.register('utils_g', utils);
|
||||||
Mota.Plugin.register('loopMap_g', loopMap, loopMap.init);
|
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 BFSFromString = `${FloorIds},${number},${number},${Dir}`;
|
||||||
type BFSToString = `${FloorIds},${number},${number}`;
|
type BFSToString = `${FloorIds},${number},${number}`;
|
||||||
@ -200,3 +200,294 @@ export function getMapData(
|
|||||||
|
|
||||||
return (bfsCache[floorId] = res);
|
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>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
import Scroll from '../components/scroll.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 { cancelGlobalDrag, isMobile, useDrag, useWheel } from '../plugin/use';
|
||||||
import {
|
import {
|
||||||
LeftOutlined,
|
LeftOutlined,
|
||||||
@ -121,10 +126,6 @@ const noBorder = ref(true);
|
|||||||
const tradition = ref(false);
|
const tradition = ref(false);
|
||||||
let scale =
|
let scale =
|
||||||
((isMobile ? 1.5 : 3) * mainSetting.getValue('ui.mapScale', 100)) / 100;
|
((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);
|
noBorder.value = core.getLocalStorage('noBorder', true);
|
||||||
tradition.value = core.getLocalStorage('flyTradition', false);
|
tradition.value = core.getLocalStorage('flyTradition', false);
|
||||||
@ -133,32 +134,34 @@ const floor = computed(() => {
|
|||||||
return core.status.maps[nowFloor.value];
|
return core.status.maps[nowFloor.value];
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(nowFloor, draw);
|
watch(nowFloor, n => {
|
||||||
|
drawer.nowFloor = n;
|
||||||
|
draw();
|
||||||
|
});
|
||||||
watch(nowArea, n => {
|
watch(nowArea, n => {
|
||||||
ox = 0;
|
|
||||||
oy = 0;
|
|
||||||
scale = 3;
|
scale = 3;
|
||||||
lastScale = 3;
|
lastScale = 3;
|
||||||
|
drawer.nowArea = n;
|
||||||
|
drawer.scale = 3;
|
||||||
|
drawer.ox = 0;
|
||||||
|
drawer.oy = 0;
|
||||||
if (area[n] && !area[n].includes(nowFloor.value))
|
if (area[n] && !area[n].includes(nowFloor.value))
|
||||||
nowFloor.value =
|
nowFloor.value =
|
||||||
area[n].find(v => v === core.status.floorId) ?? area[n][0];
|
area[n].find(v => v === core.status.floorId) ?? area[n][0];
|
||||||
});
|
});
|
||||||
watch(noBorder, n => {
|
watch(noBorder, n => {
|
||||||
core.setLocalStorage('noBorder', n);
|
core.setLocalStorage('noBorder', n);
|
||||||
drawedThumbnail = {};
|
drawer.noBorder = true;
|
||||||
drawMap();
|
drawer.drawedThumbnail = {};
|
||||||
|
drawer.drawMap();
|
||||||
});
|
});
|
||||||
watch(tradition, n => {
|
watch(tradition, n => {
|
||||||
core.setLocalStorage('flyTradition', n);
|
core.setLocalStorage('flyTradition', n);
|
||||||
});
|
});
|
||||||
|
|
||||||
const temp = document.createElement('canvas');
|
|
||||||
const tempCtx = temp.getContext('2d')!;
|
|
||||||
let map: HTMLCanvasElement;
|
let map: HTMLCanvasElement;
|
||||||
let mapCtx: CanvasRenderingContext2D;
|
|
||||||
let thumb: HTMLCanvasElement;
|
let thumb: HTMLCanvasElement;
|
||||||
let thumbCtx: CanvasRenderingContext2D;
|
let thumbCtx: CanvasRenderingContext2D;
|
||||||
let downloadMode = false;
|
|
||||||
|
|
||||||
function exit() {
|
function exit() {
|
||||||
mainUi.close(props.num);
|
mainUi.close(props.num);
|
||||||
@ -169,192 +172,7 @@ const title = computed(() => {
|
|||||||
});
|
});
|
||||||
const titleChange = createChangable(title).change;
|
const titleChange = createChangable(title).change;
|
||||||
|
|
||||||
/**
|
let drawer: MinimapDrawer;
|
||||||
* 绘制小地图
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawRight() {
|
function drawRight() {
|
||||||
let w = thumb.width;
|
let w = thumb.width;
|
||||||
@ -388,26 +206,14 @@ function drawRight() {
|
|||||||
* 绘制所有内容
|
* 绘制所有内容
|
||||||
*/
|
*/
|
||||||
function draw() {
|
function draw() {
|
||||||
drawedThumbnail = {};
|
drawer.clearCache();
|
||||||
thumbnailLoc = {};
|
drawer.drawMap();
|
||||||
drawMap();
|
|
||||||
drawRight();
|
drawRight();
|
||||||
}
|
}
|
||||||
|
|
||||||
function download() {
|
function download() {
|
||||||
if (nowArea.value === '') {
|
drawer.download();
|
||||||
tip('error', '当前地图不在任意一个区域内!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
downloadMode = true;
|
|
||||||
const before = scale;
|
|
||||||
scale = 32;
|
|
||||||
drawMap();
|
|
||||||
downloadCanvasImage(temp, nowArea.value);
|
|
||||||
scale = before;
|
|
||||||
downloadMode = false;
|
|
||||||
draw();
|
draw();
|
||||||
tip('success', '图片下载成功!');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fly() {
|
function fly() {
|
||||||
@ -418,18 +224,19 @@ function fly() {
|
|||||||
let lastScale = scale;
|
let lastScale = scale;
|
||||||
const changeScale = debounce((s: number) => {
|
const changeScale = debounce((s: number) => {
|
||||||
map.style.transform = '';
|
map.style.transform = '';
|
||||||
drawedThumbnail = {};
|
drawer.drawedThumbnail = {};
|
||||||
drawMap();
|
drawer.scale = s;
|
||||||
|
drawer.drawMap();
|
||||||
lastScale = s;
|
lastScale = s;
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
function resize(delta: number) {
|
function resize(delta: number) {
|
||||||
ox *= delta;
|
drawer.ox *= delta;
|
||||||
oy *= delta;
|
drawer.oy *= delta;
|
||||||
scale = delta * scale;
|
scale = delta * scale;
|
||||||
changeScale(scale);
|
changeScale(scale);
|
||||||
map.style.transform = `scale(${scale / lastScale})`;
|
map.style.transform = `scale(${scale / lastScale})`;
|
||||||
thumbnailLoc = {};
|
drawer.thumbnailLoc = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastX = 0;
|
let lastX = 0;
|
||||||
@ -445,12 +252,12 @@ function drag(x: number, y: number) {
|
|||||||
if (touchScale) return;
|
if (touchScale) return;
|
||||||
const dx = x - lastX;
|
const dx = x - lastX;
|
||||||
const dy = y - lastY;
|
const dy = y - lastY;
|
||||||
ox += dx;
|
drawer.ox += dx;
|
||||||
oy += dy;
|
drawer.oy += dy;
|
||||||
lastX = x;
|
lastX = x;
|
||||||
lastY = y;
|
lastY = y;
|
||||||
checkMoveThumbnail();
|
drawer.checkMoveThumbnail();
|
||||||
drawToTarget();
|
drawer.drawToTarget();
|
||||||
if (Math.abs(x - startX) > 10 || Math.abs(y - startY) > 10) moved = true;
|
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 x = e.offsetX * devicePixelRatio;
|
||||||
const y = e.offsetY * devicePixelRatio;
|
const y = e.offsetY * devicePixelRatio;
|
||||||
for (const [id, [left, top, right, bottom]] of Object.entries(
|
for (const [id, [left, top, right, bottom]] of Object.entries(
|
||||||
thumbnailLoc
|
drawer.thumbnailLoc
|
||||||
) as [FloorIds, Loc2][]) {
|
) as [FloorIds, Loc2][]) {
|
||||||
if (x >= left && x <= right && y >= top && y <= bottom) {
|
if (x >= left && x <= right && y >= top && y <= bottom) {
|
||||||
if (id === nowFloor.value) {
|
if (id === nowFloor.value) {
|
||||||
@ -497,7 +304,7 @@ function changeFloorByDelta(delta: number) {
|
|||||||
}
|
}
|
||||||
nowFloor.value = core.floorIds[to];
|
nowFloor.value = core.floorIds[to];
|
||||||
changeAreaByFloor(nowFloor.value);
|
changeAreaByFloor(nowFloor.value);
|
||||||
locateMap(nowFloor.value);
|
drawer.locateMap(nowFloor.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeFloorByDir(dir: Dir) {
|
function changeFloorByDir(dir: Dir) {
|
||||||
@ -509,30 +316,13 @@ function changeFloorByDir(dir: Dir) {
|
|||||||
|
|
||||||
if (d === dir) {
|
if (d === dir) {
|
||||||
const target = to.split(',')[0] as FloorIds;
|
const target = to.split(',')[0] as FloorIds;
|
||||||
locateMap(target);
|
drawer.locateMap(target);
|
||||||
nowFloor.value = target;
|
nowFloor.value = target;
|
||||||
return;
|
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);
|
gameKey.use(props.ui.symbol);
|
||||||
@ -607,10 +397,13 @@ function touchmove(e: TouchEvent) {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
map = document.getElementById('fly-map') as HTMLCanvasElement;
|
map = document.getElementById('fly-map') as HTMLCanvasElement;
|
||||||
mapCtx = map.getContext('2d')!;
|
|
||||||
thumb = document.getElementById('fly-thumbnail') as HTMLCanvasElement;
|
thumb = document.getElementById('fly-thumbnail') as HTMLCanvasElement;
|
||||||
thumbCtx = thumb.getContext('2d')!;
|
thumbCtx = thumb.getContext('2d')!;
|
||||||
|
|
||||||
|
drawer = new MinimapDrawer(map);
|
||||||
|
drawer.scale = scale;
|
||||||
|
drawer.noBorder = noBorder.value;
|
||||||
|
|
||||||
const mapStyle = getComputedStyle(map);
|
const mapStyle = getComputedStyle(map);
|
||||||
const thumbStyle = getComputedStyle(thumb);
|
const thumbStyle = getComputedStyle(thumb);
|
||||||
map.width = parseFloat(mapStyle.width) * devicePixelRatio;
|
map.width = parseFloat(mapStyle.width) * devicePixelRatio;
|
||||||
@ -622,7 +415,7 @@ onMounted(async () => {
|
|||||||
v.addEventListener('click', e => (v as HTMLElement).blur());
|
v.addEventListener('click', e => (v as HTMLElement).blur());
|
||||||
});
|
});
|
||||||
|
|
||||||
locateMap(nowFloor.value);
|
drawer.locateMap(nowFloor.value);
|
||||||
draw();
|
draw();
|
||||||
|
|
||||||
useDrag(
|
useDrag(
|
||||||
|
Loading…
Reference in New Issue
Block a user