diff --git a/idea.md b/idea.md index 61d5c4d..35e57d7 100644 --- a/idea.md +++ b/idea.md @@ -97,7 +97,7 @@ dam4.png ---- 存档 59 [] 技能树允许自动升级 [] 重构装备系统 [] 弹幕系统 -[] 优化各种 ui +[x] 优化各种 ui [] 怪物脚下加入阴影 [x] 着色器特效 [x] 完全删除 core.plugin,采用 Plugin.register 的形式进行插件编写 diff --git a/public/project/floors/MT50.js b/public/project/floors/MT50.js index 9bab3d2..216c65e 100644 --- a/public/project/floors/MT50.js +++ b/public/project/floors/MT50.js @@ -37,7 +37,7 @@ main.floors.MT50= "左边两个机关门在打完左下角区域的boss后开启,右边同理。" ], "9,1": [ - "左下角和右下角两个区域打完之后必须要点开学习技能,不然会卡关。", + "建议优先点出学习技能,对于特定场景将会非常有帮助", "本区域可以使用跳跃技能,不要忘记了。" ] }, diff --git a/src/game/game.ts b/src/game/game.ts index a2ed03f..cbab83c 100644 --- a/src/game/game.ts +++ b/src/game/game.ts @@ -33,21 +33,17 @@ class GameLoading extends EventEmitter { } 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'); + } } /** diff --git a/src/plugin/game/hook.ts b/src/plugin/game/hook.ts index 4f78a30..23486e9 100644 --- a/src/plugin/game/hook.ts +++ b/src/plugin/game/hook.ts @@ -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是该道具所在的坐标 diff --git a/src/plugin/game/index.ts b/src/plugin/game/index.ts index 5374d54..84b7915 100644 --- a/src/plugin/game/index.ts +++ b/src/plugin/game/index.ts @@ -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); diff --git a/src/plugin/ui/fly.ts b/src/plugin/ui/fly.ts index 5f126b9..9c5fca7 100644 --- a/src/plugin/ui/fly.ts +++ b/src/plugin/ui/fly.ts @@ -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> = {}; + thumbnailLoc: Partial> = {}; + + // 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; + } +} diff --git a/src/ui/fly.vue b/src/ui/fly.vue index 473a986..119bfe1 100644 --- a/src/ui/fly.vue +++ b/src/ui/fly.vue @@ -89,7 +89,12 @@