diff --git a/public/project/plugins.js b/public/project/plugins.js
index cb5522e..00ee286 100644
--- a/public/project/plugins.js
+++ b/public/project/plugins.js
@@ -1683,1179 +1683,6 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
}
};
},
- changeFly: async function () {
- // 该插件可自定义空间很大,自定义内容请看注释
-
- // ------------------------- 安装说明 ------------------------- //
- // 先安装基于canvas的sprite化插件(2.10.0以上自带)
- // 再将以下代码复制进插件中,并将第一行的 function () 改为 async function ()
- // 提供的api请看以this.xxx = function开头的函数,函数前会有函数说明及参数说明
-
- // ------------------------- 使用说明 ------------------------- //
- /*
- * 直接复制进插件中,然后添加一个快捷键或道具效果为core.plugin.drawFlyMap()即可使用,不需额外设置
- * 楼层id中不要出现下划线
- * 该插件具体功能有:
- * 1.绘制区域内的地图
- * 2.可以拖动地图
- * 3.点击地图可直接传送至目标地图,同时降低背景的不透明度,方便观察
- * 4.滚轮或双指可以放缩绘制内容
- * 5.放缩较大时,绘制地图的缩略图,可能会比较卡,但移动不会卡
- * 6.整合漏怪检测,如果想忽略怪物,请在下方改动或用脚本修改core.plugin.ignoreEnemies,类型为数组
- * 7.整合区域显示,所有单独或连在一起的地图会被视为一个区域
- * 8.键盘操作,上下左右移动
- */
-
- // ------------------------- 插件说明 ------------------------- //
- /*
- * 该插件注释极其详细,可以帮助那些想要提升代码力,但实力有不足的作者
- * 注意!!!该插件难度极大,没有代码底力的不建议研究
- * 该插件涉及部分较为高级的算法,如bfs
- */
-
- // 录像验证直接干掉这个插件
- if (main.replayChecking || main.mode === 'editor') return;
-
- // 延迟初始化,就不用安装到sprite化插件之后了
- await new Promise(res => setTimeout(res, 500));
-
- // ----- 不可自定义 杂七杂八的变量
- /** @type {{[x: string]: BFSResult}} */
- let mapCache = {}; // 地图缓存
- let drawCache = {}; // 绘制信息缓存
- let status = 'none'; // 当前的绘制状态
- /** @type {{[x: string]: Sprite}} */
- let sprites = {}; // 当前所有的sprite
- /** @type {{[x: string]: Sprite}} */
- let canDrag = {}; // 可以拖拽的sprite
- /** @type {{[x: string]: Button}} */
- let areaSprite = {}; // 区域列表对应的sprite
- let clicking = false; // 是否正在点击,用于拖拽判定
- let drawingMap = ''; // 正在绘制的中心楼层
- let nowScale = 0; // 当前绘制的放缩比例
- let lastTouch = {}; // 上一次的单点点击信息
- let lastLength = 0; // 手机端缩放时上一次的两指间距离
- let nowDepth = 0; // 当前的遍历深度
- let drawedThumbnail = {}; // 已经绘制过的缩略图
- let moved = false; // 鼠标按下后是否移动了
- let noBorder = false; // 是否是无边框拼接模式
- let lastScale = 0; // 上一次缩放,用于优化缩略图绘制
- let showEnemy = false; // 是否显示漏怪
- let areaPage = 0; // 区域显示的当前页数
- let nowArea = 0; // 当前区域index
- let selecting = ''; // 选择时当前正在选择的地图
-
- // ---- 不可自定义,常量
- /** @type {Area} */
- let areas = []; // 区域信息
- const perPage = Math.floor((core._PY_ - 60) / 30); // 区域的每页显示数量
-
- // ---- 可自定义,默认的切换地图的图块id
- const defaultChange = {
- left: 'leftPortal', // 左箭头
- up: 'upPortal', // 上箭头
- right: 'rightPortal', // 右箭头
- down: 'downPortal', // 下箭头
- upFloor: 'upFloor', // 上楼
- downFloor: 'downFloor' // 下楼
- };
- // ---- 可自定义,默认数值
- const defaultValue = {
- font: 'Verdana', // 默认字体
- scale: 3, // 默认地图缩放比例
- depth: Infinity // 默认的遍历深度
- };
-
- // ---- 不可自定义,计算数据
- const dirData = {
- up: [1, 0],
- down: [-1, 0],
- left: [0, 1],
- right: [0, -1],
- upFloor: [0, 0],
- downFloor: [0, 0]
- };
-
- let ignoreEnemies = (this.ignoreEnemies = []);
-
- let allChangeEntries = Object.entries(defaultChange);
-
- const reset = core.events.resetGame;
- core.events.resetGame = function () {
- reset.apply(core.events, arguments);
- areas = [];
- // 获取所有分区,使用异步函数,保证不会卡顿
- // 原理是用bfs扫,将所有连在一起的地图合并成一个区域
- (async function () {
- let all = core.floorIds.slice();
- const scanned = { [all[0]]: true };
- while (all.length > 0) {
- let now = all.shift();
- if (core.status.maps[now].deleted) continue;
- if (!now) return;
- await new Promise(res => {
- const result = bfsSearch(now, Infinity, true);
- mapCache[`${now}_Infinity_false`] = result;
- areas.push({
- name: core.floors[now].title,
- maps: result.order
- });
- for (const map of result.order) {
- scanned[map] = true;
- all = all.filter(v => !result.order.includes(v));
- }
- res('success');
- });
- }
- })();
- };
-
- /** 工具按钮 */
- class Button extends Sprite {
- constructor(
- name,
- x,
- y,
- w,
- h,
- text,
- fontSize = '20px',
- transition = true
- ) {
- const btn = super(x, y, w, h, 1050, 'game', name);
- this.css(transition);
- setTimeout(() => btn.setCss(`opacity: 1;`), 50);
- const ctx = btn.context;
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
- core.fillText(
- ctx,
- text,
- w / 2,
- h / 2,
- '#fff',
- `${fontSize} normal`,
- w - 10
- );
- sprites[name] = btn;
- }
-
- css(transition) {
- this.setCss(
- 'transition: opacity 0.6s linear, transform 0.2s linear;' +
- 'background-color: #aaa;' +
- 'box-shadow: 0px 0px 0px black;' +
- (transition ? 'opacity: 0;' : '') +
- 'filter: drop-shadow(1px 1px 2px black);' +
- 'box-shadow: 0px 0px 4px black;' +
- 'cursor: pointer;'
- );
- }
- }
-
- /** 背景 */
- class Back extends Sprite {
- constructor(name, x, y, w, h, z, color) {
- const sprite = super(x, y, w, h, z, 'game', name);
- sprites[name] = sprite;
- this.setCss(`transition: all 0.6s linear;`);
- setTimeout(() => {
- this.setCss(`background-color: ${color};`);
- }, 50);
- }
- }
-
- /**
- * 获取绘制信息
- * @param {string?} center 中心地图id
- * @param {number?} depth 搜索深度
- * @param {boolean?} noCache 是否不使用缓存
- * @returns {MapDrawInfo}
- */
- this.getMapDrawInfo = function (
- center = core.status.floorId,
- depth = defaultValue.depth,
- noCache = false
- ) {
- nowDepth = depth;
- drawingMap = center;
- const id = `${center}_${depth}_${noBorder}`;
- // 检查缓存
- if (drawCache[id] && !noCache) return drawCache[id];
- const map = bfsSearch(center, depth, noCache);
- mapCache[id] = map;
- const res = getDrawInfo(map.res, center, map.order);
- res.upOrDown = map.upOrDown;
- drawCache[id] = res;
- return res;
- };
-
- /**
- * 绘制大地图,可拖动、滚轮缩放、点击对应位置可以楼传等
- * @param {string} floorId 中心地图的id
- * @param {number} depth 遍历深度
- * @param {boolean} noCache 是否不使用缓存
- * @param {number} scale 绘制的缩放比例
- */
- this.drawFlyMap = function (
- floorId = core.status.floorId,
- depth = defaultValue.depth,
- noCache = false,
- scale = defaultValue.scale
- ) {
- if (core.isReplaying()) return;
-
- // 把区域页码归零
- nowArea = areas.findIndex(v =>
- v.maps.includes(core.status.floorId)
- );
- areaPage = 0;
- nowScale = scale;
- selecting = floorId;
- const info = this.getMapDrawInfo(floorId, depth, noCache);
- if (status !== 'scale' && status !== 'border') {
- drawBack();
- drawTools();
- }
- drawMap(info, scale);
- status = 'flyMap';
- core.lockControl();
- core.canvas.data.canvas.style.zIndex = '990';
- };
-
- /**
- * 获得某个区域的剩余怪物
- * @param {string} floorId 区域包含的地图或要扫描的地图
- * @param {boolean} area 是否扫描整个区域
- * @returns {RemainEnemy} 怪物总数、所在地图、位置
- * 返回值格式:{
- * rough: 每种怪物的数量及所有怪物的总数,为字符串,每个怪物独占一行
- * detail: 每个怪物的所在位置,每个怪物独占一行,以每20个整合成字符串,为字符串数组形式
- * data: 怪物数量的原始信息,格式为{ 楼层id: { 'x,y': 怪物id } }
- * }
- */
- this.getRemainEnemy = function (
- floorId = core.status.floorId,
- area = false
- ) {
- const res = bfsSearch(floorId, Infinity, false);
- // 整合怪物总数
- /** @type {{[x: string]: number}} */
- const category = {};
- const toShow = area ? res.order : [floorId];
- const strArr = [];
- const add = (...num) => num.reduce((pre, cur) => pre + cur, 0);
- const name = id => core.material.enemys[id].name;
- const title = id => core.status.maps[id].title;
- for (const id of toShow) {
- const enemies = res.enemies[id];
- Object.values(enemies).forEach(v => {
- // 编辑器不支持 ??=,悲
- category[v] = category[v] ?? 0;
- category[v]++;
- });
- // 每个怪物的信息
- strArr.push(
- ...Object.entries(enemies).map(
- v =>
- `${name(v[1])} 楼层:${title(
- id
- )},楼层id:${id},坐标:${v[0]}`
- )
- );
- }
- // 输出字符串
- const all = `当前${area ? '区域' : '地图'}中剩余怪物数量:${add(
- ...Object.values(category)
- )}`;
- const classified = Object.entries(category).map(
- v => `${name(v[0])} × ${v[1]}`
- ).join`\n`;
- const detail = [];
- while (strArr.length > 0) {
- detail.push(strArr.splice(0, 20).join`\n`);
- }
- return {
- rough: `${all}\n${classified}`,
- detail,
- data: res.enemies
- };
- };
-
- /**
- * 广度优先搜索搜索地图路径
- * @param {string} center 中心地图的id
- * @param {number} depth 搜索深度
- * @param {boolean} noCache 是否不使用缓存
- * @returns {BFSResult} 格式:floorId_x_y_dir: floorId_x_y
- */
- function bfsSearch(center, depth, noCache) {
- // 检查缓存
- const id = `${center}_${depth}_${noBorder}`;
- if (mapCache[id] && !noCache) return mapCache[id];
- const used = { [center]: true }; // 搜索过的楼层
- let queue = [];
- let stack = [center]; // 当前栈
- let nowDepth = -1;
- const mapOrder = [center]; // 遍历顺序,顺便还能记录遍历了哪些楼层
-
- const res = {}; // 输出结果,格式:floorId_x_y_dir: floorId_x_y
- const enemies = {};
- const upOrDown = {};
-
- // 开始循环搜索
- while (nowDepth < depth && stack.length > 0) {
- const now = stack.shift(); // 当前id
- if (core.status.maps[now].deleted) continue;
- const blocks = core.getMapBlocksObj(now); // 获取当前地图的每点的事件
- enemies[now] = {};
- // 遍历,获取可以传送的点,只检测绿点事件,因此可用红点事件进行传送来实现分区功能
- for (const i in blocks) {
- const block = blocks[i];
- // 整合漏怪检测,所以要检测怪物
- if (block.event.trigger === 'battle') {
- const id = block.event.id;
- if (ignoreEnemies.includes(id)) continue;
- else enemies[now][i] = block.event.id;
- continue;
- }
- // 检测触发器是否为切换楼层,不是则直接跳过
- if (block.event.trigger !== 'changeFloor') continue;
- const dirEntries = allChangeEntries.find(
- v => v[1] === block.event.id
- );
- // 如果不是那六种传送门,直接忽略
- if (!dirEntries) continue;
- const data = block.event.data;
- const dir = dirEntries[0];
- const route = `${now}_${i.replace(',', '_')}_${dir}`;
- const target = `${data.floorId}_${data.loc.join('_')}`;
- if (!used[data.floorId]) {
- if (dir === 'upFloor' || dir === 'downFloor') {
- upOrDown[now] = upOrDown[id] ?? [];
- upOrDown[now].push(dir);
- }
- queue.push(data.floorId); // 没有搜索过,则加入栈中
- mapOrder.push(data.floorId);
- used[data.floorId] = true;
- }
- res[route] = target;
- }
- if (stack.length === 0) {
- stack = queue;
- queue = [];
- nowDepth++;
- }
- if (stack.length === 0 && queue.length === 0) break;
- }
- return { res, order: mapOrder, enemies, upOrDown };
- }
-
- /**
- * 提供地图的绘制信息
- * @param {{[x: string]: string}} map 要绘制的地图,格式:floorId_x_y_dir: floorId_x_y
- * @param {string} center 中心地图的id
- * @param {string[]} order 遍历顺序
- * @returns {MapDrawInfo} 地图的绘制信息
- */
- function getDrawInfo(map, center, order) {
- // 先根据地图id分类,从而确定每个地图连接哪些地图,同时方便处理
- const links = {};
- for (const i in map) {
- const splitted = i.split('_');
- const id = splitted[0];
- if (!links[id]) links[id] = {};
- links[id][i] = map[i];
- }
- // 分类完毕,然后根据连接点先计算出各个地图的坐标,然后再进行判断
- const centerFloor = core.status.maps[center];
- const visitedCenter = core.hasVisitedFloor(center);
- const locs = {
- // 格式:[中心x, 中心y, 宽, 高, 是否到达过]
- [center]: [
- 0,
- 0,
- centerFloor.width,
- centerFloor.height,
- visitedCenter
- ]
- };
- const lines = {}; // 地图间的连线
- // 可以上楼下楼的地图
- const upOrDown = {};
- for (const id of order) {
- const now = links[id];
- // 遍历每一个地图的连接情况
- for (const from in now) {
- const to = now[from];
- // 先根据from to计算物理位置
- const fromData = from.split('_'),
- toData = to.split('_');
- const dir = fromData[3];
- if (dir === 'upFloor' || dir === 'downFloor') continue;
- if (!defaultChange[dir]) continue;
- const v = dirData[dir][1], // 竖直数值
- h = dirData[dir][0], // 水平数值
- ha = Math.abs(h),
- va = Math.abs(v);
- const fx = parseInt(fromData[1]), // fromX
- fy = parseInt(fromData[2]), // fromY
- tx = parseInt(toData[1]), // toX
- ty = parseInt(toData[2]), // toY
- ff = id, // fromFloorId
- tf = toData[0]; // toFloorId
- const fromFloor = core.status.maps[ff],
- toFloor = core.status.maps[tf];
- const fhw = Math.floor(fromFloor.width / 2), // fromFloorHalfWidth
- fhh = Math.floor(fromFloor.height / 2),
- thw = Math.floor(toFloor.width / 2),
- thh = Math.floor(toFloor.height / 2);
- const fLoc = locs[id] ?? [0, 0];
- if (!locs[ff]) continue;
- let x, y;
- const dis = noBorder ? 1 : 5;
- if (locs && locs[tf]) {
- x = locs[tf][0];
- y = locs[tf][1];
- } else {
- // 计算坐标,公式可以通过画图推断出
- x =
- fLoc[0] -
- ha * (fhw - fx + tx - thw) -
- v * (fhw + thw + dis);
- y =
- fLoc[1] -
- va * (fhh - fy + ty - thh) -
- h * (fhh + thh + dis);
- }
- locs[tf] = locs[tf] ?? [
- x,
- y,
- toFloor.width,
- toFloor.height,
- core.hasVisitedFloor(tf)
- ];
- // 添加连线
- lines[`${from}_${to}`] = [
- [
- fx - fhw + locs[ff][0],
- fy - fhh + locs[ff][1],
- x + tx - thw,
- y + ty - thh
- ]
- ];
- }
- }
- // 获取地图绘制需要的长宽
- let width = 0,
- height = 0;
- let left, right, up, down;
- for (const id in locs) {
- const [x, y, w, h] = locs[id];
- if (left === void 0) {
- left = right = x;
- up = down = y;
- }
- left = Math.min(x - w / 2 - 1, left);
- right = Math.max(x + w / 2 + 1, right);
- up = Math.min(y - h / 2 - 1, up);
- down = Math.max(y + h / 2 + 1, down);
- }
- width = right - left;
- height = down - up;
- // 所有地图和连线向右下移动,避免绘制出现问题
- for (const id in locs) {
- const loc = locs[id];
- loc[0] -= left; // 这时候left和up是负值,所以要减
- loc[1] -= up;
- }
- for (const route in lines) {
- const line = lines[route];
- for (const node of line) {
- node[0] -= left;
- node[1] -= up;
- node[2] -= left;
- node[3] -= up;
- }
- }
-
- return { locs, lines, width, height, layer: upOrDown };
- }
-
- /** 绘制背景 */
- function drawBack() {
- if (status !== 'none') return;
- new Back(
- '__map_back__',
- 0,
- 0,
- core._PX_,
- core._PY_,
- 175,
- 'rgba(0, 0, 0, 0.9)'
- );
- const listen = new Sprite(
- 0,
- 0,
- core._PX_,
- core._PY_,
- 1000,
- 'game',
- '__map_listen__'
- );
- addDrag(listen);
- const exit = new Button(
- '__map_exit__',
- core._PX_ - 64,
- core._PY_ - 26,
- 60,
- 22,
- '退出'
- );
- exit.addEventListener('click', close);
- sprites.listen = listen;
- }
-
- /** 绘制工具栏 */
- function drawTools() {
- new Back(
- '__map_toolback__',
- 0,
- core._PY_ - 30,
- core._PX_,
- 30,
- 600,
- 'rgba(200, 200, 200, 0.9)'
- );
- // 无边框
- const border = new Button(
- '__map_border__',
- core._PX_ - 150,
- core._PY_ - 26,
- 60,
- 22,
- '边框'
- );
- border.addEventListener('click', changeBorder);
- // 怪物数量
- const enemy = new Button(
- '__map_enemy__',
- core._PX_ - 240,
- core._PY_ - 26,
- 60,
- 22,
- '怪物'
- );
- enemy.addEventListener('click', triggerEnemy);
- // 区域显示
- const area = new Back(
- '__map_areasback__',
- core._PX_ - 80,
- 0,
- 80,
- core._PY_ - 30,
- 550,
- 'rgba(200, 200, 200, 0.9)'
- );
- drawAreaList();
- core.drawLine(
- area.context,
- 0,
- core._PY_ - 30,
- 80,
- core._PY_ - 30,
- '#222',
- 2
- );
- }
-
- /** 绘制区域列表 */
- function drawAreaList(transition = true) {
- const start = perPage * areaPage;
- Object.values(areaSprite).forEach(v => v.destroy());
- areaSprite = {};
- for (let i = start; i < start + perPage && areas[i]; i++) {
- const n = i % perPage;
- const { name, maps } = areas[i];
- const btn = new Button(
- `_area_${maps[0]}`,
- core._PX_ - 75,
- 4 + 30 * n,
- 70,
- 22,
- name,
- '16px',
- transition
- );
- areaSprite[maps[0]] = btn;
- if (i === nowArea) btn.setCss(`border: 2px solid gold;`);
- btn.addEventListener('click', () => {
- if (i === nowArea) return;
- changeArea(i);
- });
- }
- // 上一页下一页
- if (areaPage > 0) {
- const last = new Button(
- '_area_last_',
- core._PX_ - 75,
- core._PY_ - 50,
- 30,
- 16,
- '上一页',
- '14px',
- transition
- );
- areaSprite._area_last_ = last;
- last.addEventListener('click', () => {
- areaPage--;
- drawAreaList(false);
- });
- }
- if (areaPage < Math.floor(areas.length / perPage)) {
- const next = new Button(
- '_area_next_',
- core._PX_ - 35,
- core._PY_ - 50,
- 30,
- 16,
- '下一页',
- '14px',
- transition
- );
- areaSprite._area_next_ = next;
- next.addEventListener('click', () => {
- areaPage++;
- drawAreaList(false);
- });
- }
- }
-
- /**
- * 绘制大地图
- * @param {MapDrawInfo} info 地图绘制信息
- * @param {number} scale 地图的绘制比例
- */
- function drawMap(info, scale = defaultValue.scale) {
- if (status === 'flyMap') return;
- const PX = core._PX_,
- PY = core._PY_;
- const w = info.width * scale,
- h = info.height * scale;
- const id = `__flyMap__`;
- const cx = PX / 2 - w / 2,
- cy = PY / 2 - h / 2;
- const map = new Sprite(cx, cy, w, h, 500, 'game', id);
- sprites[id] = map;
- canDrag[id] = map;
- map.canvas.className = 'fly-map';
- const ctx = map.context;
- core.clearMap(ctx);
- if (!noBorder) {
- const drawed = {}; // 绘制过的线
- // 先绘制连线
- const lines = info.lines;
- for (const route in lines) {
- const line = lines[route];
- for (const node of line) {
- const from = `${node[0]},${node[1]}`,
- to = `${node[2]},${node[3]}`;
- if (drawed[`${from}-${to}`] || drawed[`${to}-${from}`])
- continue;
- drawed[`${from}-${to}`] = true;
- let lineWidth = scale / 2;
- core.drawLine(
- ctx,
- node[0] * scale,
- node[1] * scale,
- node[2] * scale,
- node[3] * scale,
- '#fff',
- lineWidth
- );
- }
- }
- // 再绘制楼层
- const locs = info.locs;
- for (const id in locs) {
- const loc = locs[id];
- let color = '#000';
- if (!loc[4]) color = '#f0f';
- const [x, y, w, h] = loc.map(
- v => typeof v === 'number' && v * scale
- );
- let dx = 0,
- dy = 0; // 避免绘图误差
- if (loc[2] % 2 === 0) dx = 0.5 * scale;
- if (loc[3] % 2 === 0) dy = 0.5 * scale;
- const fx = x - w / 2 - dx,
- fy = y - h / 2 - dy;
- core.fillRect(ctx, fx, fy, w, h, color);
- if (id === selecting)
- core.strokeRect(ctx, fx, fy, w, h, 'gold', scale / 2);
- else core.strokeRect(ctx, fx, fy, w, h, '#fff', scale / 2);
- const layer = info.upOrDown[id];
- const min = Math.min(w, h);
- if (layer?.includes('upFloor'))
- core.drawIcon(
- ctx,
- defaultChange.upFloor,
- fx,
- fy,
- min / 3,
- min / 3
- );
- if (layer?.includes('downFloor'))
- core.drawIcon(
- ctx,
- defaultChange.downFloor,
- fx + w - min / 3,
- fy + h - min / 3,
- min / 3,
- min / 3
- );
- // 显示漏怪数量
- if (showEnemy) {
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
- const c = `${drawingMap}_${nowDepth}_${noBorder}`;
- const n = Object.keys(mapCache[c].enemies[id]).length;
- color = '#3f3';
- if (n > 0) color = '#fff';
- if (n > 10) color = '#fc3';
- if (n > 20) color = '#f22';
- ctx.shadowBlur = 0.6 * nowScale;
- ctx.shadowColor = '#000';
- core.fillText(
- ctx,
- `怪物数量:${n}`,
- x,
- y,
- color,
- `${2 * nowScale}px normal`
- );
- ctx.shadowBlur = 0;
- }
- }
- }
- checkThumbnail();
- }
-
- /**
- * 重新绘制缩略图
- * @param {Sprite} sprite
- * @param {string} floor
- */
- function drawThumbnail(sprite, floor, x, y, w, h) {
- const ctx = sprite.context;
- const scale = nowScale;
- core.drawThumbnail(floor, void 0, {
- ctx: ctx,
- x: x - w / 2,
- y: y - h / 2,
- damage: true,
- all: true,
- size: Math.max(w, h) / Math.max(core._PX_, core._PY_),
- fromMap: true
- });
- const color = floor === core.status.floorId ? 'gold' : '#fff';
- if (!noBorder)
- core.strokeRect(
- ctx,
- x - w / 2,
- y - h / 2,
- w,
- h,
- color,
- scale / 2
- );
- }
-
- /** 检查是否需要绘制缩略图 */
- function checkThumbnail() {
- const id = `${drawingMap}_${nowDepth}_${noBorder}`;
- const locs = drawCache[id].locs;
- const map = canDrag[`__flyMap__`];
- for (const id in locs) {
- const loc = locs[id];
- const scale = nowScale;
- const [x, y, w, h] = loc.map(
- v => typeof v === 'number' && v * scale
- );
- let dx = 0,
- dy = 0; // 避免绘图误差
- if (loc[2] % 2 === 0) dx = 0.5 * scale;
- if (loc[3] % 2 === 0) dy = 0.5 * scale;
- if (
- !drawedThumbnail[id] &&
- x + map.x > 0 &&
- x + map.x < core._PX_ &&
- y + map.y > 0 &&
- y + map.y < core._PY_
- ) {
- if (!noBorder && core.hasVisitedFloor(id) && scale > 5) {
- drawThumbnail(map, id, x - dx, y - dy, w, h);
- drawedThumbnail[id] = true;
- }
- if (noBorder) {
- drawThumbnail(map, id, x - dx, y - dy, w, h);
- drawedThumbnail[id] = true;
- if (!core.hasVisitedFloor(id))
- core.fillRect(
- map.context,
- x - dx - w / 2,
- y - dy - h / 2,
- w,
- h,
- 'rgba(255,0,255,0.2)'
- );
- }
- }
- }
- // 如果是无边框模式,那就只绘制当前地图的边框
- if (noBorder) {
- const loc = locs[selecting];
- const scale = nowScale;
- if (loc) {
- const [x, y, w, h] = loc.map(
- v => typeof v === 'number' && v * scale
- );
- core.strokeRect(
- map.context,
- x - w / 2,
- y - h / 2,
- w,
- h,
- 'gold',
- scale / 2
- );
- }
- }
- }
-
- /** 检查点击点是否在以x,y为中心的某一矩形中 */
- function inRect(x, y, w, h, px, py) {
- x -= w / 2;
- y -= h / 2;
- return px > x && px < x + w && py > y && py < y + h;
- }
-
- /** 测试画布是否超过上限,摘自https://github.com/jhildenbiddle/canvas-size */
- function canvasTest(size) {
- const width = Math.max(Math.ceil(size[0]), 1);
- const height = Math.max(Math.ceil(size[1]), 1);
- if (width === 0 || height === 0) return true;
- const fill = [width - 1, height - 1, 1, 1];
- let cropCvs, testCvs;
- cropCvs = document.createElement('canvas');
- cropCvs.width = 1;
- cropCvs.height = 1;
- testCvs = document.createElement('canvas');
- testCvs.width = width;
- testCvs.height = height;
- const cropCtx = cropCvs.getContext('2d');
- const testCtx = testCvs.getContext('2d');
- if (testCtx) {
- testCtx.fillRect.apply(testCtx, fill);
- cropCtx.drawImage(
- testCvs,
- width - 1,
- height - 1,
- 1,
- 1,
- 0,
- 0,
- 1,
- 1
- );
- }
- const isTestPass =
- cropCtx && cropCtx.getImageData(0, 0, 1, 1).data[3] !== 0;
- return isTestPass;
- }
-
- /** 检查浏览器限制 */
- function checkMaximum(before, scale) {
- for (const id in canDrag) {
- const sprite = canDrag[id];
- const rate = scale / before;
- const w = sprite.width * rate * core.domStyle.scale,
- h = sprite.height * rate * core.domStyle.scale;
- const valid = canvasTest([w, h]);
- if (!valid) {
- core.drawTip('画布大小将超过浏览器限制!请勿继续放大!');
- return true;
- }
- }
- return false;
- }
-
- /** 关闭事件 */
- function close() {
- document.body.removeEventListener('keyup', keyboard);
- Object.values(sprites).forEach(v => {
- v.setCss('transition: opacity 0.6s linear;');
- });
- setTimeout(() => {
- Object.values(sprites).forEach(v => {
- v.setCss('opacity: 0;');
- });
- }, 50);
- setTimeout(() => {
- core.unlockControl();
- Object.values(sprites).forEach(v => {
- v.destroy();
- });
- drawedThumbnail = {};
- sprites = {};
- canDrag = {};
- status = 'none';
- core.canvas.data.canvas.style.zIndex = '170';
- }, 650);
- }
-
- /**
- * 点击地图事件,尝试楼层传送
- * @param {MouseEvent} e
- */
- function clickMap(e) {
- if (moved) return (moved = false);
- const { x, y } = core.actions._getClickLoc(e.clientX, e.clientY);
- let px = x / core.domStyle.scale,
- py = y / core.domStyle.scale;
- const scale = nowScale;
- const id = `${drawingMap}_${nowDepth}_${noBorder}`;
- const locs = drawCache[id].locs;
- const sprite = canDrag.__flyMap__;
- px -= sprite.x;
- py -= sprite.y;
- for (const id in locs) {
- const loc = locs[id];
- const [x, y, w, h] = loc.map(
- v => typeof v === 'number' && v * scale
- );
- if (inRect(x, y, w, h, px, py)) {
- return flyTo(id);
- }
- }
- }
-
- /** 飞向某个楼层 */
- function flyTo(id) {
- if (!core.hasItem('fly')) return core.drawTip('你没有楼层传送器');
- sprites.__map_back__.setCss('opacity: 0.2;');
- return core.flyTo(id, () =>
- setTimeout(() => {
- if (sprites.__map_back__) core.lockControl();
- }, 100)
- );
- }
-
- /**
- * 拖拽事件
- * @param {MouseEvent} e
- */
- function drag(e) {
- if (!clicking) return;
- const scale = core.domStyle.scale;
- moveEle(e.movementX / scale, e.movementY / scale);
- }
-
- /**
- * 手机端点击拖动事件
- * @param {TouchEvent} e
- * @this {HTMLCanvasElement}
- */
- function touchDrag(e) {
- moved = true;
- const scale = core.domStyle.scale;
- if (e.touches.length === 1) {
- // 拖拽
- const info = e.touches[0];
- if (!lastTouch[this.id]) {
- lastTouch[this.id] = [info.clientX, info.clientY];
- return;
- }
- const { clientX: x, clientY: y } = info;
- const dx = x - lastTouch[this.id][0],
- dy = y - lastTouch[this.id][1];
- moveEle(dx / scale, dy / scale);
- lastTouch[this.id] = [info.clientX, info.clientY];
- } else if (e.touches.length >= 2) {
- // 双指放缩
- const [first, second] = e.touches;
- const dx = first.clientX - second.clientX,
- dy = first.clientY - second.clientY;
- if (lastLength === 0) {
- lastLength = Math.sqrt(dx * dx + dy * dy);
- return;
- }
- let cx = (first.clientX + second.clientX) / 2,
- cy = (first.clientY + second.clientY) / 2;
- const { x, y } = core.actions._getClickLoc(cx, cy);
- cx = x / scale;
- cy = y / scale;
- const length = Math.sqrt(dx * dx + dy * dy);
- const delta = length / lastLength;
- const info = {};
- for (const id in canDrag) {
- const sprite = canDrag[id];
- const sx = sprite.x + sprite.width / 2,
- sy = sprite.y + sprite.height / 2;
- const dx = sx - mx,
- dy = sy - my;
- info[id] = [cx + dx * delta, cy + dy * delta];
- }
- scaleMap(delta * nowScale, info);
- }
- }
-
- /**
- * 滚轮缩放
- * @param {WheelEvent} e
- */
- function wheel(e) {
- const delta = 1 - Math.sign(e.deltaY) / 10;
- const { x, y } = core.actions._getClickLoc(e.clientX, e.clientY);
- const scale = core.domStyle.scale;
- const mx = x / scale,
- my = y / scale;
- const info = {};
- for (const id in canDrag) {
- const sprite = canDrag[id];
- const cx = sprite.x + sprite.width / 2,
- cy = sprite.y + sprite.height / 2;
- const dx = cx - mx,
- dy = cy - my;
- info[id] = [mx + dx * delta, my + dy * delta];
- }
- scaleMap(delta * nowScale, info);
- }
-
- /** 切换边框 */
- function changeBorder() {
- noBorder = !noBorder;
- redraw('border');
- }
-
- /** 切换是否显示漏怪数量 */
- function triggerEnemy() {
- showEnemy = !showEnemy;
- redraw('enemy');
- }
-
- /** 改变区域 */
- function changeArea(index) {
- nowArea = index;
- drawAreaList(false);
- drawedThumbnail = {};
- status = 'area';
- nowScale = defaultValue.scale;
- drawMap(core.plugin.getMapDrawInfo(areas[index].maps[0]));
- }
-
- /** 重绘 */
- function redraw(id, px, py, move = true) {
- const { x, y } = canDrag.__flyMap__;
- status = id;
- drawedThumbnail = {};
- drawMap(
- core.plugin.getMapDrawInfo(drawingMap, nowDepth, true),
- nowScale
- );
- if (move) canDrag.__flyMap__.move(px ?? x, py ?? y);
- checkThumbnail();
- }
-
- /**
- * 拖拽时移动需要元素
- * @param {string} dx
- * @param {string} dy
- */
- function moveEle(dx, dy) {
- moved = true;
- for (const id in canDrag) {
- const sprite = canDrag[id];
- const ctx = sprite.context;
- sprite.x += dx;
- sprite.y += dy;
- core.relocateCanvas(ctx, dx, dy, true);
- }
- checkThumbnail();
- }
-
- /**
- * 缩放绘制地图
- * @param {number} target 目标缩放比例
- * @param {{[x: string]: [number, number]}} info 缩放后的sprite位置数据
- */
- function scaleMap(target, info) {
- // 检查浏览器限制
- if (checkMaximum(nowScale, target)) return;
- clearTimeout(lastScale);
- const [x, y] = info.__flyMap__;
- // 先直接修改style,延迟200ms再绘制,进行性能优化
- const sprite = canDrag.__flyMap__;
- const rate = target / nowScale;
- nowScale = target;
- sprite.resize(sprite.width * rate, sprite.height * rate, true);
- sprite.move(x - sprite.width / 2, y - sprite.height / 2);
- lastScale = setTimeout(() => {
- redraw('scale', x - sprite.width / 2, y - sprite.height / 2);
- }, 200);
- }
-
- /** 键盘操作
- * @param {KeyboardEvent} e
- */
- function keyboard(e) {
- if (
- e.key === 'Enter' ||
- e.key === 'C' ||
- e.key === 'c' ||
- e.key === ''
- ) {
- return flyTo(selecting);
- } else if (e.key === 'Escape' || e.key === 'x' || e.key === 'X') {
- return close();
- } else if (e.key.startsWith('Arrow')) {
- const dir = e.key.slice(5).toLowerCase();
- // 获取目标楼层
- const res =
- mapCache[`${drawingMap}_${nowDepth}_${noBorder}`].res;
- const key = Object.keys(res).find(v => {
- const [floorId, x, y, d] = v.split('_');
- return floorId === selecting && d === dir;
- });
- if (!key) return;
- const target = res[key].split('_')[0];
- selecting = target;
- redraw('key');
- }
- }
-
- /**
- * 给需要的元素添加拖拽等事件
- * @param {HTMLCanvasElement} ele
- */
- function addDrag(ele) {
- ele.addEventListener('wheel', wheel);
- ele.addEventListener('mousemove', drag);
- ele.addEventListener('touchmove', touchDrag);
- ele.addEventListener('click', clickMap);
- ele.addEventListener('mousedown', () => {
- clicking = true;
- });
- ele.addEventListener('mouseup', () => {
- clicking = false;
- });
- ele.addEventListener('touchend', () => {
- lastTouch = {};
- lastLength = 0;
- });
- document.body.addEventListener('keyup', keyboard);
- }
- },
towerBoss: function () {
// 智慧boss
// 变量们
diff --git a/src/components/colomn.vue b/src/components/colomn.vue
index 8606a16..cd3db25 100644
--- a/src/components/colomn.vue
+++ b/src/components/colomn.vue
@@ -23,11 +23,13 @@