HumanBreak/src/plugin/ui/fly.ts

270 lines
7.4 KiB
TypeScript
Raw Normal View History

2023-01-01 00:06:45 +08:00
import { has } from '../utils';
2022-12-31 21:44:33 +08:00
export default function init() {
2023-01-01 22:02:58 +08:00
return { splitArea, getMapDrawData };
2022-12-31 21:44:33 +08:00
}
2023-01-01 00:06:45 +08:00
type BFSFromString = `${FloorIds},${number},${number},${Dir}`;
type BFSToString = `${FloorIds},${number},${number}`;
2022-12-31 22:45:32 +08:00
interface MapBFSResult {
maps: FloorIds[];
2023-01-01 00:06:45 +08:00
link: Record<BFSFromString, BFSToString>;
2022-12-31 22:45:32 +08:00
}
2023-01-01 22:02:58 +08:00
interface MapDrawData {
locs: Partial<Record<FloorIds, LocArr>>;
line: [number, number, number, number][];
width: number;
height: number;
}
let area: Record<string, FloorIds[]> = {};
2022-12-31 22:45:32 +08:00
const bfsCache: Partial<Record<FloorIds, MapBFSResult>> = {};
2023-01-01 22:02:58 +08:00
/**
* FloorIds,interval,border
*/
const drawCache: Record<string, MapDrawData> = {};
2022-12-31 22:45:32 +08:00
2023-01-01 00:06:45 +08:00
const arrow: Partial<Record<AllIds, Dir>> = {
leftPortal: 'left',
rightPortal: 'right',
upPortal: 'up',
downPortal: 'down'
};
2023-01-01 22:02:58 +08:00
/**
*
*/
export function splitArea() {
const used: FloorIds[] = [];
for (const id of core.floorIds) {
if (used.includes(id) || core.status.maps[id].deleted) continue;
const data = getMapData(id, true);
used.push(...data.maps);
if (data.maps.length > 0) {
const title = core.status.maps[id].title;
area[title] = data.maps;
}
}
}
export function getArea() {
return area;
}
/**
*
* @param floorId
* @param interval
* @param border
* @param noCache 使
*/
export function getMapDrawData(
floorId: FloorIds,
interval: number = 5,
border: number = 1,
noCache: boolean = false
): MapDrawData {
const id = `${floorId},${interval},${border}`;
if (drawCache[id] && !noCache) return drawCache[id];
const { link, maps } = getMapData(floorId, noCache);
const locs: Partial<Record<FloorIds, LocArr>> = {};
const line: [number, number, number, number][] = [];
const center = core.status.maps[floorId];
let left = -center.width / 2,
right = center.width / 2,
top = -center.height / 2,
bottom = center.height / 2;
for (const [from, to] of Object.entries(link)) {
const [fromId, fxs, fys, dir] = from.split(',') as [
FloorIds,
string,
string,
Dir
];
const [toId, txs, tys] = to.split(',') as [FloorIds, string, string];
const fromMap = core.status.maps[fromId];
const toMap = core.status.maps[toId];
const fx = parseInt(fxs),
fy = parseInt(fys),
tx = parseInt(txs),
ty = parseInt(tys);
const fw = fromMap.width,
fh = fromMap.height;
const tw = toMap.width,
th = toMap.height;
locs[fromId] ??= [0, 0];
const [fromX, fromY] = locs[fromId]!;
if (!locs[toId]) {
const dx = core.utils.scan[dir].x,
dy = core.utils.scan[dir].y;
const toX =
fromX +
(fx - fw / 2) -
(tx - tw / 2) +
(border * 2 + interval) * dx,
toY =
fromY +
(fy - fh / 2) -
(ty - th / 2) +
(border * 2 + interval) * dy;
// 地图位置和连线位置
locs[toId] = [toX, toY];
}
const [toX, toY] = locs[toId]!;
line.push([
fromX + (fx - fw / 2 + 0.5),
fromY + (fy - fh / 2 + 0.5),
toX + (tx - tw / 2 + 0.5),
toY + (ty - th / 2 + 0.5)
]);
// 计算地图总长宽
const l = toX - tw / 2,
r = toX + tw / 2,
t = toY - th / 2,
b = toY + th / 2;
if (l < left) left = l;
if (r > right) right = r;
if (t < top) top = t;
if (b > bottom) bottom = b;
}
// 移动位置,居中
Object.values(locs).forEach(v => {
v[0] -= left;
v[1] -= top;
});
line.forEach(v => {
v[0] -= left;
v[2] -= left;
v[1] -= top;
v[3] -= top;
});
left -= 5;
right += 5;
top -= 5;
bottom += 5;
const res = { locs, line, width: right - left, height: bottom - top };
2022-12-31 22:45:32 +08:00
2023-01-01 22:02:58 +08:00
return (drawCache[id] = res);
}
2022-12-31 21:44:33 +08:00
2022-12-31 22:45:32 +08:00
/**
* 广
* @param floorId id
* @param noCache 使
*/
export function getMapData(
floorId: FloorIds,
noCache: boolean = false
): MapBFSResult {
2023-01-01 00:06:45 +08:00
if (has(bfsCache[floorId]) && !noCache) return bfsCache[floorId]!;
2022-12-31 22:45:32 +08:00
const queue = [floorId];
2023-01-01 00:06:45 +08:00
const used: Partial<Record<FloorIds, boolean>> = {
[floorId]: true
};
const floors = [floorId];
const link: Record<BFSFromString, BFSToString> = {};
2022-12-31 22:45:32 +08:00
while (queue.length > 0) {
const now = queue.shift()!;
2023-01-01 00:06:45 +08:00
const change = core.floors[now].changeFloor;
const blocks = core.getMapBlocksObj(now);
for (const [loc, ev] of Object.entries(change)) {
const target = ev.floorId as FloorIds;
if (target.startsWith(':')) continue;
const block = blocks[loc as LocString];
const id = block.event.id;
if (id in arrow) {
if (!used[target]) {
2023-01-01 22:02:58 +08:00
const from = `${now},${loc},${arrow[id]}` as BFSFromString;
const to = `${target},${ev.loc![0]},${
ev.loc![1]
}` as BFSToString;
link[from] = to;
2023-01-01 00:06:45 +08:00
queue.push(target);
floors.push(target);
}
}
}
used[now] = true;
2022-12-31 22:45:32 +08:00
}
2023-01-01 00:06:45 +08:00
2023-01-01 22:02:58 +08:00
const res = {
2023-01-01 00:06:45 +08:00
maps: floors,
link
};
2023-01-01 22:02:58 +08:00
return (bfsCache[floorId] = res);
}
/**
*
* @param ctx
* @param floorId
* @param interval
* @param border
* @param noCache 使
*/
export function drawFlyMap(
ctx: CanvasRenderingContext2D,
floorId: FloorIds,
offset: [number, number],
size: [number, number],
scale: number = 3,
interval: number = 5,
border: number = 1,
noCache: boolean = false
) {
const data = getMapDrawData(floorId, interval, border, noCache);
const [ox, oy] = offset;
const [width, height] = size;
const canvas = ctx.canvas;
canvas.width = data.width * devicePixelRatio * scale;
canvas.height = data.height * devicePixelRatio * scale;
ctx.lineWidth = border * devicePixelRatio * scale;
ctx.strokeStyle = '#fff';
ctx.scale(scale, scale);
// 绘制连线
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
][]) {
drawThumbnail(ctx, id, scale, [x, y], offset, size);
}
}
function drawThumbnail(
ctx: CanvasRenderingContext2D,
floorId: FloorIds,
scale: number,
pos: [number, number],
offset: [number, number],
size: [number, number]
) {
const [x, y] = pos;
const [ox, oy] = offset;
const [width, height] = size;
const map = core.status.maps[floorId];
if (
ox + x * scale + (map.width * scale) / 2 < 0 ||
ox + x * scale - (map.width * scale) / 2 > width ||
oy + y * scale + (map.height * scale) / 2 < 0 ||
oy + y * scale - (map.height * scale) / 2 > height
) {
return;
}
2022-12-31 22:45:32 +08:00
}