From b3602d3f21a0de42e4eb9d0acf31b0a88dbcdb25 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sun, 1 Jan 2023 22:02:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A5=BC=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 1 + public/libs/maps.js | 19 +- public/project/functions.js | 1 + public/project/items.js | 2 +- public/project/plugins.js | 5 + public/styles.css | 9 +- src/plugin/ui/fly.ts | 213 +++++++++- src/plugin/uiController.ts | 8 +- src/styles.less | 2 + src/types/action.d.ts | 2 + src/types/function.d.ts | 2 +- src/types/map.d.ts | 30 ++ src/types/plugin.d.ts | 13 +- src/ui/book.vue | 1 - src/ui/desc.vue | 7 +- src/ui/fly.vue | 748 ++++++++++++++++++++++++++++++++++++ src/ui/settings.vue | 8 +- src/ui/skill.vue | 5 - 18 files changed, 1040 insertions(+), 36 deletions(-) create mode 100644 src/ui/fly.vue diff --git a/components.d.ts b/components.d.ts index 96dcded..4e8ab88 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,6 +11,7 @@ declare module '@vue/runtime-core' { ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] ASlider: typeof import('ant-design-vue/es')['Slider'] + ASwitch: typeof import('ant-design-vue/es')['Switch'] Box: typeof import('./src/components/box.vue')['default'] BoxAnimate: typeof import('./src/components/boxAnimate.vue')['default'] Colomn: typeof import('./src/components/colomn.vue')['default'] diff --git a/public/libs/maps.js b/public/libs/maps.js index 684651b..a2cfa58 100644 --- a/public/libs/maps.js +++ b/public/libs/maps.js @@ -2685,8 +2685,8 @@ maps.prototype._drawThumbnail_drawToTarget = function (floorId, options) { y = options.y || 0, size = options.size || 1; // size的含义改为(0,1]范围的系数以适配长方形,默认为1,楼传为3/4,SL界面为0.3 - var w = Math.ceil(size * core._PX_), - h = Math.ceil(size * core._PY_); + var w = size * core._PX_, + h = size * core._PY_; // 特判是否为编辑器,编辑器中长宽均采用core.js的遗留正方形像素边长,以保证下面的绘制正常 if (main.mode == 'editor') w = h = size * core.__PIXELS__; var width = core.floors[floorId].width, @@ -2697,6 +2697,21 @@ maps.prototype._drawThumbnail_drawToTarget = function (floorId, options) { if (centerY == null) centerY = Math.floor(height / 2); var tempCanvas = core.bigmap.tempCanvas; + if (options.inFlyMap) { + ctx.drawImage( + tempCanvas.canvas, + 0, + 0, + tempCanvas.canvas.width, + tempCanvas.canvas.height, + options.x, + options.y, + options.w, + options.h + ); + return; + } + const scale = core.domStyle.scale * devicePixelRatio; if (options.all) { var tempWidth = tempCanvas.canvas.width, diff --git a/public/project/functions.js b/public/project/functions.js index 8348a5b..9a46c00 100644 --- a/public/project/functions.js +++ b/public/project/functions.js @@ -52,6 +52,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { // 隐藏右下角的音乐按钮 core.dom.musicBtn.style.display = 'none'; core.dom.enlargeBtn.style.display = 'none'; + core.splitArea(); }, win: function (reason, norank, noexit) { // 游戏获胜事件 diff --git a/public/project/items.js b/public/project/items.js index 0c2899a..20ccb96 100644 --- a/public/project/items.js +++ b/public/project/items.js @@ -325,7 +325,7 @@ var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "text": "可以自由往来去过的楼层", "hideInReplay": true, "hideInToolbox": true, - "useItemEffect": "core.ui.drawFly(core.floorIds.indexOf(core.status.floorId));", + "useItemEffect": "core.ui.drawFly();", "canUseItemEffect": "(function () {\n\treturn core.status.maps[core.status.floorId].canFlyFrom;\n})();" }, "coin": { diff --git a/public/project/plugins.js b/public/project/plugins.js index 2288f36..cb5522e 100644 --- a/public/project/plugins.js +++ b/public/project/plugins.js @@ -5445,6 +5445,11 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = { return (core.plugin.equipOpened.value = true); }; + ui.prototype.drawFly = function () { + if (!core.isReplaying()) + return (core.plugin.flyOpened.value = true); + }; + control.prototype.updateStatusBar_update = function () { if (!core.isPlaying() || core.hasFlag('__statistics__')) return; core.control.controldata.updateStatusBar(); diff --git a/public/styles.css b/public/styles.css index 7826d23..6ec7bdb 100644 --- a/public/styles.css +++ b/public/styles.css @@ -44,12 +44,12 @@ body { } #startPanel { - width: 100%; + width: 150%; height: 100%; position: absolute; top: 0; - left: 0; - background-color: #fff; + left: -25%; + background-color: #000; overflow: hidden; z-index: 300; } @@ -106,6 +106,7 @@ body { width: auto; transform: translate(-50%, -50%); z-index: 260; + object-fit: cover; } #startLogo { @@ -143,7 +144,7 @@ body { } #startButtonGroup { - width: 33%; + width: 25%; position: absolute; text-align: center; font-size: 1.4em; diff --git a/src/plugin/ui/fly.ts b/src/plugin/ui/fly.ts index 22beec7..323fea9 100644 --- a/src/plugin/ui/fly.ts +++ b/src/plugin/ui/fly.ts @@ -1,7 +1,7 @@ import { has } from '../utils'; export default function init() { - return { splitArea, getMapData }; + return { splitArea, getMapDrawData }; } type BFSFromString = `${FloorIds},${number},${number},${Dir}`; @@ -12,7 +12,20 @@ interface MapBFSResult { link: Record; } +interface MapDrawData { + locs: Partial>; + line: [number, number, number, number][]; + width: number; + height: number; +} + +let area: Record = {}; + const bfsCache: Partial> = {}; +/** + * 键的格式:FloorIds,interval,border + */ +const drawCache: Record = {}; const arrow: Partial> = { leftPortal: 'left', @@ -21,9 +34,125 @@ const arrow: Partial> = { downPortal: 'down' }; -export function splitArea() {} +/** + * 切分地图区域 + */ +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 getMapDrawData(floorId: FloorIds) {} +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> = {}; + 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 }; + + return (drawCache[id] = res); +} /** * 广度优先搜索地图信息 @@ -53,12 +182,12 @@ export function getMapData( const block = blocks[loc as LocString]; const id = block.event.id; if (id in arrow) { - const from = `${now},${loc},${arrow[id]}` as BFSFromString; - const to = `${target},${ev.loc![0]},${ - ev.loc![1] - }` as BFSToString; - link[from] = to; if (!used[target]) { + const from = `${now},${loc},${arrow[id]}` as BFSFromString; + const to = `${target},${ev.loc![0]},${ + ev.loc![1] + }` as BFSToString; + link[from] = to; queue.push(target); floors.push(target); } @@ -67,8 +196,74 @@ export function getMapData( used[now] = true; } - return { + const res = { maps: floors, link }; + + 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; + } } diff --git a/src/plugin/uiController.ts b/src/plugin/uiController.ts index 441a404..26208e0 100644 --- a/src/plugin/uiController.ts +++ b/src/plugin/uiController.ts @@ -7,6 +7,7 @@ import Settings from '../ui/settings.vue'; import Desc from '../ui/desc.vue'; import Skill from '../ui/skill.vue'; import SkillTree from '../ui/skillTree.vue'; +import Fly from '../ui/fly.vue'; export const bookOpened = ref(false); export const toolOpened = ref(false); @@ -16,6 +17,7 @@ export const settingsOpened = ref(false); export const descOpened = ref(false); export const skillOpened = ref(false); export const skillTreeOpened = ref(false); +export const flyOpened = ref(false); export const transition = ref(true); export const noClosePanel = ref(false); @@ -30,7 +32,8 @@ const UI_LIST: [Ref, Component][] = [ [settingsOpened, Settings], [descOpened, Desc], [skillOpened, Skill], - [skillTreeOpened, SkillTree] + [skillTreeOpened, SkillTree], + [flyOpened, Fly] ]; /** ui栈 */ @@ -63,7 +66,8 @@ export default function init() { settingsOpened, descOpened, skillOpened, - skillTreeOpened + skillTreeOpened, + flyOpened }; } diff --git a/src/styles.less b/src/styles.less index fd57455..f04fe0c 100644 --- a/src/styles.less +++ b/src/styles.less @@ -35,6 +35,8 @@ .selectable { border: #0000 0.5px solid; + padding: 1% 3% 1% 3%; + width: 100%; } .selectable[selected='true'] { diff --git a/src/types/action.d.ts b/src/types/action.d.ts index 17464a1..89c1016 100644 --- a/src/types/action.d.ts +++ b/src/types/action.d.ts @@ -132,6 +132,8 @@ interface Actions extends VoidedActionFuncs { * @param x 要判断的横坐标 */ _out(x: number): boolean; + + _getNextFlyFloor(delta: number, index: number): number; } declare const actions: new () => Actions; diff --git a/src/types/function.d.ts b/src/types/function.d.ts index f3a3a89..4edc5bb 100644 --- a/src/types/function.d.ts +++ b/src/types/function.d.ts @@ -196,7 +196,7 @@ interface EventData { * @param toId 目标楼层 * @param callback 飞到后的回调函数 */ - flyTo(toId: FloorIds, callback: () => void): void; + flyTo(toId: FloorIds, callback?: () => void): boolean; /** * 与怪物战斗前 diff --git a/src/types/map.d.ts b/src/types/map.d.ts index 7e3a0e5..dc9df48 100644 --- a/src/types/map.d.ts +++ b/src/types/map.d.ts @@ -187,6 +187,11 @@ interface Floor extends FloorBase { * 图块信息 */ blocks: Block[]; + + /** + * 是否被砍层 + */ + deleted?: boolean; } interface ResolvedFloor extends FloorBase { @@ -400,6 +405,31 @@ interface DrawThumbnailConfig { * 是否使用v2优化 */ v2: boolean; + + /** + * 是否在小地图中出现 + */ + inFlyMap: boolean; + + /** + * 小地图模式下的横坐标 + */ + x: number; + + /** + * 小地图模式下的纵坐标 + */ + y: number; + + /** + * 小地图模式下的宽度 + */ + w: number; + + /** + * 小地图模式下的高度 + */ + h: number; } interface BlockFilter { diff --git a/src/types/plugin.d.ts b/src/types/plugin.d.ts index 9285133..9a0a934 100644 --- a/src/types/plugin.d.ts +++ b/src/types/plugin.d.ts @@ -18,7 +18,8 @@ interface PluginDeclaration extends PluginUtils, PluginUis, PluginUse, - SkillTree { + SkillTree, + MiniMap { /** * 添加函数 例:添加弹出文字,像这个就可以使用core.addPop或core.plugin.addPop调用 * @param px 弹出的横坐标 @@ -149,6 +150,9 @@ interface PluginUis { /** 技能树界面是否打开 */ readonly skillTreeOpened: Ref; + /** 楼传界面是否打开 */ + readonly flyOpened: Ref; + /** ui栈 */ readonly uiStack: Ref; @@ -246,6 +250,13 @@ interface SkillTree { loadSkillTree(data: number[]): void; } +interface MiniMap { + /** + * 切分区域 + */ + splitArea(): void; +} + type Chapter = 'chapter1'; interface Skill { diff --git a/src/ui/book.vue b/src/ui/book.vue index 3af5793..d303371 100644 --- a/src/ui/book.vue +++ b/src/ui/book.vue @@ -201,7 +201,6 @@ function keydown(e: KeyboardEvent) { } onMounted(async () => { - const div = document.getElementById('book') as HTMLDivElement; if (core.plugin.transition.value) await sleep(600); else await sleep(50); document.addEventListener('keyup', keyup); diff --git a/src/ui/desc.vue b/src/ui/desc.vue index ba0dd5f..f53f401 100644 --- a/src/ui/desc.vue +++ b/src/ui/desc.vue @@ -4,7 +4,7 @@ >
@@ -53,9 +53,4 @@ function show(condition: string) { display: flex; flex-direction: column; } - -.desc-item { - padding: 1% 3% 1% 3%; - width: 100%; -} diff --git a/src/ui/fly.vue b/src/ui/fly.vue new file mode 100644 index 0000000..8b04102 --- /dev/null +++ b/src/ui/fly.vue @@ -0,0 +1,748 @@ + + + + + diff --git a/src/ui/settings.vue b/src/ui/settings.vue index 9e4d2b1..9623aea 100644 --- a/src/ui/settings.vue +++ b/src/ui/settings.vue @@ -3,7 +3,7 @@ >