diff --git a/src/plugin/fx/path.ts b/src/plugin/fx/path.ts deleted file mode 100644 index fabd328..0000000 --- a/src/plugin/fx/path.ts +++ /dev/null @@ -1,496 +0,0 @@ -import { - TimingFn, - linear, - bezierPath, - Animation, - hyper, - power, - sleep -} from 'mutate-animate'; -import { has } from '../utils'; - -interface AnimatedPathShadow { - offsetX: number | TimingFn; - offsetY: number | TimingFn; - blur: number | TimingFn; - color: string | TimingFn<4>; -} - -type AnimatedPathShadowEntry = [ - keyof AnimatedPathShadow, - ValueOf -][]; - -type AnimatedPathFilterKey = - | 'blur' - | 'brightness' - | 'contrast' - | 'grayscale' - | 'hueRotate' - | 'opacity' - | 'saturate' - | 'sepia'; - -type AnimatedPathFilter = Record; - -interface Path { - path: TimingFn<2>; - length: number; -} - -export default function init() { - return { AnimatedPath, pathTest: test }; -} - -export class AnimatedPath { - /** 不同线条间是否连接起来,不连接的话中间可能会有短暂的中断 */ - join: boolean = true; - /** 路径信息 */ - linePath: Path[] = []; - /** 绘制画布 */ - ctx: CanvasRenderingContext2D; - - private dashStatus: number = 0; - private dashLength: number = 0; - private dashMode: (number | TimingFn)[] = []; - private lineWidth: number | TimingFn = 1; - private lineColor: string | TimingFn<4> = '#fff'; - private lineShadow: Partial = {}; - private lineFilter: Partial = {}; - private pathClose: boolean = false; - - constructor(ctx: CanvasRenderingContext2D) { - this.ctx = ctx; - } - - /** - * 设置虚线格式 - * @param mode 一个偶数个元素的数组,表示虚线格式,如果不是偶数个,那么会再复制一份加到后面。 - * 元素可以填数字或一个函数,如果是数字,表示该值是定值, - * 如果是函数,该函数包括一个参数input,表示绘制动画的完成度,输出一个数字,表示虚线的长度。 - * @example path.dash([5, 10]); // 表示绘制时,会先绘制5像素的实线,之后10像素不绘制,然后再绘制5像素实线,以此类推。 - * @example path.dash([5, (input) => Math.round(input * 10)]); - * // 表示绘制时,先绘制5像素的实线,然后会有一段不绘制,不绘制的长度是动画完成度乘10,以此类推。 - */ - dash(mode: (number | TimingFn)[]): this { - const res = mode.slice(); - if (mode.length % 2 === 1) res.push(...mode); - this.dashMode = mode; - return this; - } - - /** - * 设置线条宽度 - * @param width 绘制的线条宽度,如果是数字,表示宽度是个常量。 - * 也可以传入一个函数,函数包括一个参数input,表示绘制动画的完成度,输出一个数字,表示宽度。 - * @example path.width(2); // 设置线条宽度为2像素 - * @example path.width((input) => input * 5); // 设置线条宽度为动画完成度的5倍 - */ - width(width: number | TimingFn): this { - this.lineWidth = width; - return this; - } - - /** - * 设置线条颜色 - * @param color 颜色,可以传入css颜色字符串或函数,传入字符串表示线条颜色为定值, - * 如果传入函数,这个函数有一个参数input,表示动画完成度,输出一个有4个元素的数组,表示颜色的rgba值。 - * @example path.color('#fff'); // 设置线条为白色 - * @example path.color((input) => [input * 100, input * 50, input * 255, input * 0.8]); - * // 设置颜色的红色值为动画完成度的100倍,绿色为动画完成度的50倍,蓝色为动画完成度的255倍,不透明度为动画完成度的0.8倍 - */ - color(color: string | TimingFn<4>): this { - this.lineColor = color; - return this; - } - - /** - * 设置线条阴影 - * @param shadow 阴影信息,一个对象,包含offsetX(横向偏移量), offsetY(纵向偏移量), blur(虚化程度), color(颜色) - * 四个属性,均为可选,前三个可传入数字或函数,color可传入字符串或函数,传入的函数与前面几个方法中的函数类似。 - * ```ts - * path.shadow({ - * offsetX: 3, // 横向偏移量为3 - * offsetY: input => input * 10, // 纵向偏移量为动画完成度的10倍 - * color: '#0008', // 颜色为半透明的黑色 - * blur: 4 // 虚化程度为4 - * }) - * ``` - */ - shadow(shadow: Partial): this { - this.lineShadow = shadow; - return this; - } - - /** - * 设置线条的滤镜 - * @param filter 滤镜信息,一个对象,可以有以下属性: - * 1. `blur`: 虚化程度 - * 2. `brightness`: 亮度,百分比,`0-Infinity` - * 3. `contrast`: 对比度,百分比,`0-Infinity` - * 4. `grayscale`: 黑白度,百分比,`0-100` - * 5. `hueRotate`: 色相旋转,角度,`0-360` - * 6. `invert`: 反色,百分比,`0-100` - * 7. `opacity`: 不透明度,百分比,`0-100` - * 8. `saturate`: 饱和度,百分比,`0-Infinity` - * 9. `sepia`: 深褐色(怀旧风格),百分比,`0-100` - * 以上属性均可选,均可传入数字或函数。 - * ```ts - * path.filter({ - * blur: 3, // 虚化程度为3 - * contrast: input => 100 + input * 50 // 对比度增加动画完成度的50倍 - * }) - * ``` - */ - filter(filter: Partial): this { - this.lineFilter = filter; - return this; - } - - /** - * 清空路径 - */ - clear(): this { - this.linePath = []; - return this; - } - - /** - * 添加直线 - * @param x1 起点横坐标 - * @param y1 起点纵坐标 - * @param x2 终点横坐标 - * @param y2 终点纵坐标 - * @returns - */ - line(x1: number, y1: number, x2: number, y2: number): this { - const dx = x2 - x1; - const dy = y2 - y1; - this.add(x => [x1 + dx * x, y1 + dy * x], Math.sqrt(dx ** 2 + dy ** 2)); - return this; - } - - /** - * 添加圆弧 - * @param x 圆心横坐标 - * @param y 圆心纵坐标 - * @param r 圆半径 - * @param start 圆起始角度,弧度制,水平向右表示0弧度,顺时针旋转 - * @param end 圆终止角度,弧度制 - */ - circle(x: number, y: number, r: number, start: number, end: number): this { - const dt = end - start; - this.add( - input => [ - x + r * Math.cos(dt * input + start), - y + r * Math.sin(dt * input + start) - ], - r * dt - ); - return this; - } - - /** - * 添加一个椭圆 - * @param x 圆心横坐标 - * @param y 圆心纵坐标 - * @param a 椭圆横轴长 - * @param b 椭圆纵轴长 - * @param start 起始角度,弧度制 - * @param end 终止角度,弧度制 - */ - ellipse( - x: number, - y: number, - a: number, - b: number, - start: number, - end: number - ): this { - const dt = end - start; - this.add(input => [ - x + a * Math.cos(dt * input + start), - y + b * Math.sin(dt * input + start) - ]); - return this; - } - - /** - * 添加一个矩形 - * @param x 左上角横坐标 - * @param y 左上角纵坐标 - * @param w 宽度 - * @param h 高度 - * @param lineWidth 线宽,用于闭合矩形 - */ - rect( - x: number, - y: number, - w: number, - h: number, - lineWidth: number = 0 - ): this { - const x2 = x + w; - const y2 = y + h; - this.line(x, y, x2, y) - .line(x2, y, x2, y2) - .line(x2, y2, x, y2) - .line(x, y2, x, y - lineWidth / 2); - return this; - } - - /** - * 添加一个贝塞尔曲线 - * @param point 起点,控制点,终点的坐标 - */ - bezier(...point: [number, number][]): this { - if (point.length < 2) { - throw new Error(`The point number of bezier must larger than 2.`); - } - const start = point[0]; - const end = point.at(-1)!; - const cps = point.slice(1, -1); - this.add(bezierPath(start, end, ...cps)); - return this; - } - - /** - * 添加一个路径 - * @param path 路径函数,输入一个绘制完成度,输出一个坐标 - * @param length 路径的长度,不填则会调用calLength进行计算,用于该段路径的绘制时间的计算, - * 也可以通过设置这个值来指定该段路径的绘制时间。请尽量指定该值,不然的话性能表现会较差,如果大量添加路径可能会导致卡顿。 - * @example path.add(input => [input * 100, (input * 100) ** 2]); // 添加一个抛物线路径 - */ - add(path: TimingFn<2>, length: number = this.calLength(path)): this { - this.linePath.push({ - path, - length - }); - return this; - } - - /** - * 设置路径是否闭合 - * @param close 路径是否闭合 - */ - close(close: boolean): this { - this.pathClose = close; - return this; - } - - /** - * 对一个路径进行线积分,计算其长度。注意该函数性能较差,请谨慎使用 - * @param path 路径函数,输入一个绘制完成度,输出一个坐标 - * @returns 路径的长度 - */ - calLength(path: TimingFn<2>): number { - let [lastX, lastY] = path(0); - let length = 0; - for (let i = 1; i <= 1000; i++) { - const [x, y] = path(i * 0.001); - length += Math.sqrt((x - lastX) ** 2 + (y - lastY) ** 2); - lastX = x; - lastY = y; - } - return length; - } - - /** - * 立即执行绘制,不执行动画,该函数的性能表现可能较差,请谨慎使用 - */ - drawImmediate(): this { - const totalLength = this.linePath.reduce( - (pre, cur) => pre + cur.length, - 0 - ); - let drawed = 0; - this.linePath.forEach(v => { - this.drawFrom(v.path, 0, 1, drawed, v.length); - drawed += v.length / totalLength; - }); - return this; - } - - /** - * 执行绘制 - * @param time 绘制时间,如果是0则会直接调用drawImmediate函数,注意如果时间太短的话可能会造成明显的锯齿现象 - * @param timing 绘制完成度的速率函数,输入一个时间完成度,输出一个绘制完成度 - */ - draw(time: number, timing: TimingFn = linear()): this { - const totalLength = this.linePath.reduce( - (pre, cur) => pre + cur.length, - 0 - ); - const ratio = this.linePath.map(v => v.length / totalLength); - let drawed = 0; - let now = 0; - let nowEnd = ratio[0]; - let lastComplete = 0; - this.ctx.beginPath(); - this.ctx.moveTo(...this.linePath[0].path(0)); - - const findNext = (input: number) => { - if (input >= 1 || nowEnd > input) return []; - const skipped: number[] = []; - while (1) { - drawed += ratio[now]; - now++; - nowEnd += ratio[now]; - if (input < nowEnd) { - lastComplete = drawed; - break; - } else skipped.push(now); - } - return skipped; - }; - - const ani = new Animation(); - ani.register('path', 0); - ani.mode(timing).time(time).absolute().apply('path', 1); - ani.all().then(() => { - ani.ticker.destroy(); - if (this.pathClose) { - this.ctx.beginPath(); - this.ctx.moveTo(...this.linePath.at(-1)!.path(0.999)); - this.ctx.lineTo(...this.linePath[0].path(0.001)); - this.ctx.stroke(); - } - this.ctx.closePath(); - }); - ani.ticker.add(() => { - const complete = ani.value.path; - if (complete >= nowEnd) { - const d = nowEnd - drawed; - const from = (lastComplete - drawed) / d; - this.drawFrom( - this.linePath[now].path, - from, - 1, - lastComplete, - ratio[now] - ); - const skipped = findNext(complete); - skipped.forEach(v => { - const path = this.linePath[v]; - this.drawFrom(path.path, 0, 1, lastComplete, ratio[v]); - }); - } - const fn = this.linePath[now].path; - const d = nowEnd - drawed; - const from = (lastComplete - drawed) / d; - const to = (complete - drawed) / d; - this.drawFrom(fn, from, to, lastComplete, ratio[now]); - lastComplete = complete; - }); - return this; - } - - private drawFrom( - path: TimingFn<2>, - from: number, - to: number, - complete: number, - ratio: number, - length?: number - ) { - const [fx, fy] = path(from); - const [tx, ty] = path(to); - - const l = - length ?? Math.ceil(Math.sqrt((tx - fx) ** 2 + (ty - fy) ** 2)); - const step = (to - from) / l; - const ctx = this.ctx; - let [lastX, lastY] = path(from); - for (let i = 1; i <= l; i++) { - this.handleFx(complete + (ratio * i * step) / l); - const [x, y] = path(from + step * i); - ctx.lineTo(x, y); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(lastX, lastY); - ctx.lineTo(x, y); - lastX = x; - lastY = y; - } - } - - private handleFx(complete: number) { - const ctx = this.ctx; - const width = - typeof this.lineWidth === 'number' - ? this.lineWidth - : this.lineWidth(complete); - ctx.lineWidth = width; - - let color; - if (typeof this.lineColor === 'string') { - color = this.lineColor; - } else { - const c = this.lineColor(complete); - color = `rgba(${c[0]},${c[1]},${c[2]},${c[3]})`; - } - ctx.strokeStyle = color; - - const shadow: Partial< - Record - > = {}; - for (const [key, value] of Object.entries( - this.lineShadow - ) as AnimatedPathShadowEntry) { - if (typeof value === 'function') { - const n = value(complete); - if (typeof n === 'number') { - shadow[key as Exclude] = - n; - } else { - shadow.color = `rgba(${n[0]},${n[1]},${n[2]},${n[3]})`; - } - } else { - // @ts-ignore - shadow[key] = value; - } - } - if (has(shadow.blur)) ctx.shadowBlur = shadow.blur as number; - if (has(shadow.offsetX)) ctx.shadowOffsetX = shadow.offsetX as number; - if (has(shadow.offsetY)) ctx.shadowOffsetY = shadow.offsetY as number; - if (has(shadow.color)) ctx.shadowColor = shadow.color as string; - - let filter = ''; - for (const [key, value] of Object.entries(this.lineFilter) as [ - AnimatedPathFilterKey, - number | TimingFn - ][]) { - let v; - if (typeof value === 'number') { - v = value; - } else { - v = value(complete); - } - if (key === 'blur') filter += `blur(${v}px)`; - else filter += `${key}(${v}%)`; - } - ctx.filter = filter; - } -} - -async function test() { - const ctx = core.createCanvas('test', 0, 0, 480, 480, 100); - ctx.canvas.style.backgroundColor = '#000d'; - const path = new AnimatedPath(ctx); - path.color('#fff') - .width(2) - .rect(100, 100, 280, 280, 2) - .close(true) - .draw(1000, power(5, 'in-out')); - - await sleep(1050); - path.clear() - .bezier([200, 200], [280, 200], [280, 280]) - .bezier([280, 280], [200, 280], [200, 200]) - .draw(1000, power(5, 'in-out')); - await sleep(1050); - path.clear() - .bezier([280, 200], [200, 200], [200, 280]) - .bezier([200, 280], [280, 280], [280, 200]) - .draw(1000, power(5, 'in-out')); -} diff --git a/src/plugin/game/fx/itemDetail.ts b/src/plugin/game/fx/itemDetail.ts index e19effd..50888c0 100644 --- a/src/plugin/game/fx/itemDetail.ts +++ b/src/plugin/game/fx/itemDetail.ts @@ -1,5 +1,5 @@ import { EnemyCollection, ensureFloorDamage } from '@/game/enemy/damage'; -import { checkV2, formatDamage } from '../utils'; +import { formatDamage } from '../utils'; export function init() { core.control.updateDamage = function ( diff --git a/src/plugin/game/fx/rewrite.ts b/src/plugin/game/fx/rewrite.ts deleted file mode 100644 index e3601c9..0000000 --- a/src/plugin/game/fx/rewrite.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { getEnemy } from '@/game/enemy/battle'; -import { formatDamage } from '../utils'; - -export function init() { - core.maps._initDetachedBlock = function ( - info: BlockInfo, - x: number, - y: number, - displayDamage: boolean = false - ) { - let headCanvas = null, - bodyCanvas = '__body_' + x + '_' + y, - damageCanvas = null; - - // head - if (!info.bigImage && info.height > 32) { - headCanvas = '__head_' + x + '_' + y; - core.createCanvas(headCanvas, 0, 0, 32, info.height - 32, 55); - } - // body - if (info.bigImage) { - var bigImageInfo = this._getBigImageInfo( - info.bigImage, - info.face, - info.posX - ); - const { per_width, per_height } = bigImageInfo; - core.createCanvas(bodyCanvas, 0, 0, per_width, per_height, 35); - } else { - core.createCanvas(bodyCanvas, 0, 0, 32, 32, 35); - } - - // 伤害 - if ( - info.cls.indexOf('enemy') == 0 && - core.hasItem('book') && - displayDamage - ) { - const enemy = getEnemy(x, y); - const dam = enemy?.calDamage() ?? { damage: 0 }; - const { damage, color } = formatDamage(dam.damage); - - damageCanvas = '__damage_' + x + '_' + y; - const ctx = core.createCanvas(damageCanvas, 0, 0, 32, 32, 65); - ctx.textAlign = 'left'; - ctx.font = '14px normal'; - core.fillBoldText(ctx, damage, 1, 31, color as string); - if (core.flags.displayCritical) { - const critical = enemy?.calCritical(1) ?? []; - const atk = core.formatBigNumber(critical[0]?.delta, true); - const display = atk === '???' ? '?' : atk; - core.fillBoldText(ctx, display, 1, 21, '#FFFFFF'); - } - } - return { - headCanvas, - bodyCanvas, - damageCanvas - }; - }; -} diff --git a/src/plugin/game/index.ts b/src/plugin/game/index.ts index c5f201b..97ca1b2 100644 --- a/src/plugin/game/index.ts +++ b/src/plugin/game/index.ts @@ -3,7 +3,6 @@ import * as fiveLayer from './fiveLayer'; import * as itemDetail from './fx/itemDetail'; import * as replay from './replay'; import * as ui from './ui'; -import * as rewrite from './fx/rewrite'; import * as loopMap from './loopMap'; import * as removeMap from './removeMap'; import * as shop from './shop'; @@ -27,7 +26,6 @@ Mota.Plugin.register('chase_g', chase); Mota.Plugin.register('skill_g', skill); Mota.Plugin.register('towerBoss_g', towerBoss); Mota.Plugin.register('fiveLayer_g', fiveLayer, fiveLayer.init); -Mota.Plugin.register('rewrite_g', rewrite, rewrite.init); Mota.Plugin.register('itemDetail_g', itemDetail, itemDetail.init); // Mota.Plugin.register('study_g', study); Mota.Plugin.register('remainEnemy_g', remainEnemy); diff --git a/src/plugin/utils.ts b/src/plugin/utils.ts index 0b6a38e..7b1b38a 100644 --- a/src/plugin/utils.ts +++ b/src/plugin/utils.ts @@ -6,7 +6,6 @@ import { Ref, ref } from 'vue'; import { EVENT_KEY_CODE_MAP, KeyCode } from './keyCodes'; import axios from 'axios'; import { decompressFromBase64 } from 'lz-string'; -import { parseColor } from './webgl/utils'; import { Keyboard, KeyboardEmits } from '@/core/main/custom/keyboard'; import { fixedUi, mainUi } from '@/core/main/init/ui'; import { isAssist } from '@/core/main/custom/hotkey'; @@ -341,12 +340,6 @@ export function ensureArray(arr: T): T extends any[] ? T : T[] { return arr instanceof Array ? arr : [arr]; } -export function pColor(color: string) { - const arr = parseColor(color); - arr[3] ??= 1; - return `rgba(${arr.join(',')})` as Color; -} - /** * 删除数组内的某个项,返回删除后的数组 * @param arr 要操作的数组 diff --git a/tsconfig.node.json b/tsconfig.node.json index 27a9537..238a2f5 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -7,5 +7,5 @@ "allowSyntheticDefaultImports": true, "strict": true }, - "include": ["vite.config.ts", "script/**/*.ts"] + "include": ["vite.config.ts", "script/**/*.ts", "mota.config.ts"] }