// https://github.com/jamiebuilds/tinykeys/pull/193/commits/2598ecb3db6b3948c7acbf0e7bd8b0674961ad61 import { useMessage } from "naive-ui"; import { SwipeAction, TouchAction, swipe, touch, } from "./frontcommand/scrcpyMaskCmd"; import { KeyCancelSkill, KeyDirectionalSkill, KeyDirectionlessSkill, KeyFire, KeyMacro, KeyMacroList, KeyMappingConfig, KeyObservation, KeySight, KeySteeringWheel, KeyTap, KeyTriggerWhenDoublePressedSkill, KeyTriggerWhenPressedSkill, } from "./keyMappingConfig"; import { useGlobalStore } from "./store/global"; import { LogicalPosition, getCurrent } from "@tauri-apps/api/window"; function clientxToPosx(clientx: number) { return clientx < 70 ? 0 : Math.floor((clientx - 70) * (screenSizeW / maskSizeW)); } function clientyToPosy(clienty: number) { return clienty < 30 ? 0 : Math.floor((clienty - 30) * (screenSizeH / maskSizeH)); } function clientxToPosOffsetx(clientx: number, posx: number, scale = 1) { let offsetX = clientxToPosx(clientx) - posx; return Math.round(offsetX * scale); } function clientyToPosOffsety(clienty: number, posy: number, scale = 1) { let offsetY = clientyToPosy(clienty) - posy; return Math.round(offsetY * scale); } function clientPosToSkillOffset( clientPos: { x: number; y: number }, range: number ): { offsetX: number; offsetY: number } { const maxLength = (120 / maskSizeH) * screenSizeH; const centerX = maskSizeW * 0.5; const centerY = maskSizeH * 0.5; // The center of the game display is higher than the center of the mask clientPos.y -= maskSizeH * 0.066; // w450 : h315 = 100 : 70, so the true offsetX is 0.7 * cOffsetX const cOffsetX = (clientPos.x - 70 - centerX) * 0.7; const cOffsetY = clientPos.y - 30 - centerY; const offsetD = Math.sqrt(cOffsetX ** 2 + cOffsetY ** 2); if (offsetD == 0) { return { offsetX: 0, offsetY: 0, }; } const rangeD = (maskSizeH - centerY) * range * 0.01; if (offsetD >= rangeD) { // include the case of rangeD == 0 return { offsetX: Math.round((maxLength / offsetD) * cOffsetX), offsetY: Math.round((maxLength / offsetD) * cOffsetY), }; } else { const factor = offsetD / rangeD; return { offsetX: Math.round((cOffsetX / rangeD) * maxLength * factor), offsetY: Math.round((cOffsetY / rangeD) * maxLength * factor), }; } } async function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } function calculateMacroPosX( // pos relative to the mask posX: [string, number] | number, relativeSizeW: number ): number { if (typeof posX === "number") { return Math.round(posX * (screenSizeW / relativeSizeW)); } if (typeof posX === "string") { return clientxToPosx(mouseX); } else { if (posX[0] === "mouse") { return ( clientxToPosx(mouseX) + Math.round(posX[1] * (screenSizeW / relativeSizeW)) ); } else { throw new Error("Invalid pos"); } } } function calculateMacroPosY( // pos relative to the mask posY: [string, number] | number, relativeSizeH: number ): number { if (typeof posY === "number") { return Math.round(posY * (screenSizeH / relativeSizeH)); } if (typeof posY === "string") { return clientyToPosy(mouseY); } else { if (posY[0] === "mouse") { return ( clientyToPosy(mouseY) + Math.round(posY[1] * (screenSizeH / relativeSizeH)) ); } else { throw new Error("Invalid pos"); } } } function calculateMacroPosList( posList: [[string, number] | number, [string, number] | number][], relativeSize: { w: number; h: number } ): { x: number; y: number }[] { return posList.map((posPair) => { return { x: calculateMacroPosX(posPair[0], relativeSize.w), y: calculateMacroPosY(posPair[1], relativeSize.h), }; }); } // add shortcuts for observation function addObservationShortcuts( key: string, relativeSize: { w: number; h: number }, // pos relative to the mask posX: number, posY: number, scale: number, pointerId: number ) { let observationMouseX = 0; let observationMouseY = 0; posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); addShortcut( key, async () => { observationMouseX = clientxToPosx(mouseX); observationMouseY = clientyToPosy(mouseY); await touchX(TouchAction.Down, pointerId, posX, posY); }, async () => { await touchX( TouchAction.Move, pointerId, posX + clientxToPosOffsetx(mouseX, observationMouseX, scale), posY + clientyToPosOffsety(mouseY, observationMouseY, scale) ); }, async () => { await touchX( TouchAction.Up, pointerId, posX + clientxToPosOffsetx(mouseX, observationMouseY, scale), posY + clientyToPosOffsety(mouseY, observationMouseY, scale) ); } ); } // add shortcuts for simple tap (touch when press down) function addTapShortcuts( key: string, relativeSize: { w: number; h: number }, time: number, // pos relative to the mask posX: number, posY: number, pointerId: number ) { posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); addShortcut( key, async () => { await touchX(TouchAction.Default, pointerId, posX, posY, time); }, undefined, undefined ); } // add shortcuts for cancel skill function addCancelSkillShortcuts( key: string, relativeSize: { w: number; h: number }, // pos relative to the mask posX: number, posY: number, pointerId: number ) { posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); addShortcut( key, async () => { // delete the callback for (const cancelAbleKey of cancelAbleKeyList) { loopDownKeyCBMap.delete(cancelAbleKey); upKeyCBMap.delete(cancelAbleKey); } // special case for double press skill for (const [key, val] of doublePressedDownKey) { if (val) { loopDownKeyCBMap.delete(key); doublePressedDownKey.set(key, false); } } let distance = 0; while (distance <= 20) { await touchX(TouchAction.Move, pointerId, posX + distance, posY); await sleep(5); distance += 1; } await touchX(TouchAction.Up, pointerId, posX, posY); }, undefined, undefined ); } // add shortcuts for trigger when pressed skill function addTriggerWhenPressedSkillShortcuts( key: string, relativeSize: { w: number; h: number }, // pos relative to the mask posX: number, posY: number, directional: boolean, // range: when directional is true // time: when directional is false rangeOrTime: number, pointerId: number ) { posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); if (directional) { addShortcut( key, // down async () => { // up doublepress skill await upDoublePressedKey(); const skillOffset = clientPosToSkillOffset( { x: mouseX, y: mouseY }, rangeOrTime ); await swipe({ action: SwipeAction.Default, pointerId, screen: { w: screenSizeW, h: screenSizeH, }, pos: [ { x: posX, y: posY }, { x: posX + skillOffset.offsetX, y: posY + skillOffset.offsetY, }, ], intervalBetweenPos: 0, }); }, undefined, undefined ); } else { addShortcut( key, async () => { await upDoublePressedKey(); await touchX(TouchAction.Default, pointerId, posX, posY, rangeOrTime); }, undefined, undefined ); } } // add shortcuts for trigger when double pressed skill (cancelable, but in another way) const doublePressedDownKey = new Map(); function addTriggerWhenDoublePressedSkillShortcuts( key: string, relativeSize: { w: number; h: number }, // pos relative to the mask posX: number, posY: number, range: number, pointerId: number ) { posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); doublePressedDownKey.set(key, false); addShortcut( key, // down async () => { if (doublePressedDownKey.get(key) === false) { // first press: touch down const skillOffset = clientPosToSkillOffset( { x: mouseX, y: mouseY }, range ); await swipe({ action: SwipeAction.NoUp, pointerId, screen: { w: screenSizeW, h: screenSizeH, }, pos: [ { x: posX, y: posY }, { x: posX + skillOffset.offsetX, y: posY + skillOffset.offsetY, }, ], intervalBetweenPos: 0, }); // set the flag to true doublePressedDownKey.set(key, true); // add loop CB loopDownKeyCBMap.set(key, async () => { const loopSkillOffset = clientPosToSkillOffset( { x: mouseX, y: mouseY }, range ); await touchX( TouchAction.Move, pointerId, posX + loopSkillOffset.offsetX, posY + loopSkillOffset.offsetY ); }); } else { // second press: touch up // delete the loop CB loopDownKeyCBMap.delete(key); const skillOffset = clientPosToSkillOffset( { x: mouseX, y: mouseY }, range ); await touchX( TouchAction.Up, pointerId, posX + skillOffset.offsetX, posY + skillOffset.offsetY ); // set the flag to false doublePressedDownKey.set(key, false); } }, undefined, undefined ); } // add shortcuts for directionless skill (cancelable) function addDirectionlessSkillShortcuts( key: string, relativeSize: { w: number; h: number }, // pos relative to the mask posX: number, posY: number, pointerId: number ) { posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); addShortcut( key, // down async () => { // up doublepress skill await upDoublePressedKey(); await touchX(TouchAction.Down, pointerId, posX, posY); }, // loop undefined, // up async () => { await touch({ action: TouchAction.Up, pointerId, screen: { w: screenSizeW, h: screenSizeH, }, pos: { x: posX, y: posY, }, }); }, true ); } // up all double pressed key async function upDoublePressedKey() { for (const [key, val] of doublePressedDownKey) { if (val) { await downKeyCBMap.get(key)?.(); } } } // add shortcuts for directional skill (cancelable) function addDirectionalSkillShortcuts( key: string, relativeSize: { w: number; h: number }, // pos relative to the mask posX: number, posY: number, range: number, pointerId: number ) { posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); addShortcut( key, // down async () => { // up doublepress skill await upDoublePressedKey(); const skillOffset = clientPosToSkillOffset( { x: mouseX, y: mouseY }, range ); await swipe({ action: SwipeAction.NoUp, pointerId, screen: { w: screenSizeW, h: screenSizeH, }, pos: [ { x: posX, y: posY }, { x: posX + skillOffset.offsetX, y: posY + skillOffset.offsetY, }, ], intervalBetweenPos: 0, }); }, // loop async () => { const skillOffset = clientPosToSkillOffset( { x: mouseX, y: mouseY }, range ); await touchX( TouchAction.Move, pointerId, posX + skillOffset.offsetX, posY + skillOffset.offsetY ); }, // up async () => { const skillOffset = clientPosToSkillOffset( { x: mouseX, y: mouseY }, range ); await touchX( TouchAction.Up, pointerId, posX + skillOffset.offsetX, posY + skillOffset.offsetY ); }, true ); } // add shortcuts for steering wheel function addSteeringWheelKeyboardShortcuts( key: wheelKey, relativeSize: { w: number; h: number }, // pos relative to the mask posX: number, posY: number, offset: number, pointerId: number ) { let loopFlag = false; let curPosX = 0; let curPosY = 0; posX = Math.round((posX / relativeSize.w) * screenSizeW); posY = Math.round((posY / relativeSize.h) * screenSizeH); // calculate the end coordinates of the eight directions of the direction wheel let offsetHalf = Math.round(offset / 1.414); const calculatedPosList = [ { x: posX - offset, y: posY }, // left { x: posX + offset, y: posY }, // right { x: posX, y: posY - offset }, // up { x: posX, y: posY + offset }, // down { x: posX - offsetHalf, y: posY - offsetHalf }, // left up { x: posX + offsetHalf, y: posY - offsetHalf }, // right up { x: posX - offsetHalf, y: posY + offsetHalf }, // left down { x: posX + offsetHalf, y: posY + offsetHalf }, // right down ]; // united loop callback for all directions const unitedloopCB = async () => { let newPosIndex; if (downKeyMap.get(key.left)) { newPosIndex = downKeyMap.get(key.up) ? 4 : downKeyMap.get(key.down) ? 6 : 0; } else if (downKeyMap.get(key.right)) { newPosIndex = downKeyMap.get(key.up) ? 5 : downKeyMap.get(key.down) ? 7 : 1; } else if (downKeyMap.get(key.up)) { newPosIndex = 2; } else if (downKeyMap.get(key.down)) { newPosIndex = 3; } else { // all keys released await unitedUpCB(); return; } // if newPos is the same as curPos, return let newPos = calculatedPosList[newPosIndex]; if (newPos.x === curPosX && newPos.y === curPosY) return; curPosX = newPos.x; curPosY = newPos.y; // move to the direction await touchX(TouchAction.Move, pointerId, newPos.x, newPos.y); }; const unitedUpCB = async () => { if ( downKeyMap.get(key.left) === false && downKeyMap.get(key.right) === false && downKeyMap.get(key.up) === false && downKeyMap.get(key.down) === false ) { // all wheel keys has been released for (const k of ["left", "right", "up", "down"]) { // only delete the valid key loopDownKeyCBMap.delete(key[k]); upKeyCBMap.delete(key[k]); } // touch up await touchX(TouchAction.Up, pointerId, curPosX, curPosY); // recover the status curPosX = 0; curPosY = 0; loopFlag = false; } }; for (const k of ["left", "right", "up", "down"]) { addShortcut( key[k], async () => { if (loopFlag) return; loopFlag = true; // add upCB upKeyCBMap.set(key[k], unitedUpCB); // touch down on the center position await touchX(TouchAction.Down, pointerId, posX, posY); // add loopCB loopDownKeyCBMap.set(key[k], unitedloopCB); }, undefined, undefined ); } } interface wheelKey { left: string; right: string; up: string; down: string; [key: string]: string; } // add baisc click shortcuts function addClickShortcuts(key: string, pointerId: number) { addShortcut( key, async () => { await touchX( TouchAction.Down, pointerId, clientxToPosx(mouseX), clientyToPosy(mouseY) ); }, async () => { await touchX( TouchAction.Move, pointerId, clientxToPosx(mouseX), clientyToPosy(mouseY) ); }, async () => { await touchX( TouchAction.Up, pointerId, clientxToPosx(mouseX), clientyToPosy(mouseY) ); } ); } function addSightShortcuts( relativeSize: { w: number; h: number }, sightKeyMapping: KeySight, fireKeyMapping?: KeyFire ) { // TODO 2. i18n 3. 单独函数,同时配合可视化组件 4. 组件配置中唯一 const appWindow = getCurrent(); let mouseLock = false; let msgReactive: ReturnType | null = null; const key = "KeyH"; const sightClientX = 70 + sightKeyMapping.posX; const sightClientY = 30 + sightKeyMapping.posY; const sightDeviceX = Math.round( (sightKeyMapping.posX / relativeSize.w) * screenSizeW ); const sightDeviceY = Math.round( (sightKeyMapping.posY / relativeSize.h) * screenSizeH ); const removeShortcut = (key: string) => { loopDownKeyCBMap.delete(key); downKeyCBMap.delete(key); upKeyCBMap.delete(key); downKeyMap.delete(key); }; const touchRelateToSight = async (action: TouchAction) => { await touchX( action, sightKeyMapping.pointerId, sightDeviceX + clientxToPosOffsetx(mouseX, sightDeviceX, sightKeyMapping.scaleX), sightDeviceY + clientyToPosOffsety(mouseY, sightDeviceY, sightKeyMapping.scaleY) ); }; const sightLoopCB = async () => { await touchRelateToSight(TouchAction.Move); }; // only scaleX and scaleY are different from sightLoopCB const fireNoDragLoopCB = fireKeyMapping ? async () => { await touchX( TouchAction.Move, sightKeyMapping.pointerId, sightDeviceX + clientxToPosOffsetx(mouseX, sightDeviceX, fireKeyMapping.scaleX), sightDeviceY + clientyToPosOffsety(mouseY, sightDeviceY, fireKeyMapping.scaleY) ); } : undefined; const fireDragLoopCB = fireKeyMapping ? async () => { await touchX( TouchAction.Move, fireKeyMapping.pointerId, fireKeyMapping.posX + accOffsetX + clientxToPosOffsetx(mouseX, sightDeviceX, fireKeyMapping.scaleX), fireKeyMapping.posX + accOffsetY + clientyToPosOffsety(mouseY, sightDeviceY, fireKeyMapping.scaleY) ); } : undefined; let accOffsetX = 0; let accOffsetY = 0; const moveLeaveHandler = async () => { if (fireKeyMapping && fireKeyMapping.drag && downKeyMap.get("M0")) { // fire drag mode // stop fireDragLoopCB loopDownKeyCBMap.delete(key); // cal accOffset accOffsetX += clientxToPosOffsetx( mouseX, sightDeviceX, fireKeyMapping.scaleX ); accOffsetY += clientyToPosOffsety( mouseY, sightDeviceY, fireKeyMapping.scaleY ); // move mouse await appWindow.setCursorPosition( new LogicalPosition(sightClientX, sightClientY) ); mouseX = sightClientX; mouseY = sightClientY; // start fireDragLoopCB loopDownKeyCBMap.set(key, fireDragLoopCB!); } else { // sight mode or fire without drag mode const fireFlag = fireKeyMapping && !fireKeyMapping.drag && downKeyMap.get("M0"); // stop sightLoopCB or fireNoDragLoopCB loopDownKeyCBMap.delete(key); // touch up if (fireFlag) { await touchX( TouchAction.Move, sightKeyMapping.pointerId, sightDeviceX + clientxToPosOffsetx(mouseX, sightDeviceX, fireKeyMapping.scaleX), sightDeviceY + clientyToPosOffsety(mouseY, sightDeviceY, fireKeyMapping.scaleY) ); } else { await touchRelateToSight(TouchAction.Up); } await sleep(150); // move mouse await appWindow.setCursorPosition( new LogicalPosition(sightClientX, sightClientY) ); mouseX = sightClientX; mouseY = sightClientY; // touch down await touchX( TouchAction.Down, sightKeyMapping.pointerId, sightDeviceX, sightDeviceY ); // start sightLoopCB or fireNoDragLoopCB loopDownKeyCBMap.set(key, fireFlag ? fireNoDragLoopCB! : sightLoopCB); } }; // add sight shortcut addShortcut(key, async () => { if (mouseLock) { // stop sight mode loopDownKeyCBMap.delete(key); await touchRelateToSight(TouchAction.Up); await appWindow.setCursorVisible(true); maskElement.removeEventListener("mouseleave", moveLeaveHandler); mouseLock = false; if (msgReactive) { msgReactive.destroy(); msgReactive = null; } // remove fire key if (fireKeyMapping) { removeShortcut("M0"); } // add click addClickShortcuts("M0", 0); } else { // start sight mode await appWindow.setCursorVisible(false); maskElement.addEventListener("mouseleave", moveLeaveHandler); mouseLock = true; msgReactive = message.info(`鼠标已锁定, 按 ${key} 键解锁`, { duration: 0, }); await appWindow.setCursorPosition( new LogicalPosition(sightClientX, sightClientY) ); mouseX = sightClientX; mouseY = sightClientY; await touchX( TouchAction.Down, sightKeyMapping.pointerId, sightDeviceX, sightDeviceY ); loopDownKeyCBMap.set(key, sightLoopCB); // remove click removeShortcut("M0"); // add fire key if (fireKeyMapping) { // fire with drag addShortcut( "M0", async () => { // stop sightLoopCB loopDownKeyCBMap.delete(key); // touch up sight await touchRelateToSight(TouchAction.Up); if (!fireKeyMapping.drag) { // touch down sight await touchX( TouchAction.Down, sightKeyMapping.pointerId, sightDeviceX, sightDeviceY ); } else { // clear accumulated offset accOffsetX = 0; accOffsetY = 0; } // move cursor await appWindow.setCursorPosition( new LogicalPosition(sightClientX, sightClientY) ); mouseX = sightClientX; mouseY = sightClientY; // touch down fire await touchX( TouchAction.Down, fireKeyMapping.pointerId, fireKeyMapping.posX, fireKeyMapping.posY ); // start fireDragLoopCB or fireNoDragLoopCB loopDownKeyCBMap.set( key, fireKeyMapping.drag ? fireDragLoopCB! : fireNoDragLoopCB! ); }, undefined, async () => { // stop fireDragLoopCB or fireNoDragLoopCB loopDownKeyCBMap.delete(key); // touch up fire await touchX( TouchAction.Up, fireKeyMapping.pointerId, fireKeyMapping.posX + clientxToPosOffsetx( mouseX, sightDeviceX, fireKeyMapping.scaleX ), fireKeyMapping.posY + clientyToPosOffsety(mouseY, sightDeviceY, fireKeyMapping.scaleY) ); // touch down sight await touchX( TouchAction.Down, sightKeyMapping.pointerId, sightDeviceX, sightDeviceY ); // move cursor await appWindow.setCursorPosition( new LogicalPosition(sightClientX, sightClientY) ); mouseX = sightClientX; mouseY = sightClientY; // start sightLoopCB loopDownKeyCBMap.set(key, sightLoopCB); } ); } } }); } let screenSizeW: number; let screenSizeH: number; let maskSizeW: number; let maskSizeH: number; let mouseX = 0; let mouseY = 0; let maskElement: HTMLElement; let message: ReturnType; const downKeyMap: Map = new Map(); const downKeyCBMap: Map Promise> = new Map(); const loopDownKeyCBMap: Map Promise> = new Map(); const upKeyCBMap: Map Promise> = new Map(); const cancelAbleKeyList: string[] = []; function handleKeydown(event: KeyboardEvent) { event.preventDefault(); if (event.repeat) return; if (downKeyMap.has(event.code)) { downKeyMap.set(event.code, true); // execute the down callback (if there is) asyncily let cb = downKeyCBMap.get(event.code); if (cb) cb(); } } function handleKeyup(event: KeyboardEvent) { event.preventDefault(); if (downKeyMap.has(event.code)) { downKeyMap.set(event.code, false); // execute the up callback (if there is) asyncily let cb = upKeyCBMap.get(event.code); if (cb) cb(); } } function handleMouseDown(event: MouseEvent) { if (event.target !== maskElement) return; mouseX = event.clientX; mouseY = event.clientY; event.preventDefault(); let key = "M" + event.button.toString(); if (downKeyMap.has(key)) { downKeyMap.set(key, true); // execute the down callback asyncily let cb = downKeyCBMap.get(key); if (cb) cb(); } } function handleMouseUp(event: MouseEvent) { mouseX = event.clientX; mouseY = event.clientY; event.preventDefault(); let key = "M" + event.button.toString(); if (downKeyMap.has(key) && downKeyMap.get(key)) { downKeyMap.set(key, false); // execute the up callback asyncily let cb = upKeyCBMap.get(key); if (cb) cb(); } } function handleMouseMove(event: MouseEvent) { mouseX = event.clientX; mouseY = event.clientY; } let lastWheelDownTime: number = 0; let lastWheelUpTime: number = 0; function handleMouseWheel(event: WheelEvent) { event.preventDefault(); // trigger interval is 50ms if (event.deltaY > 0 && event.timeStamp - lastWheelDownTime > 50) { lastWheelDownTime = event.timeStamp; // WheelDown downKeyCBMap.get("WheelDown")?.(); upKeyCBMap.get("WheelDown")?.(); } else if (event.deltaY < 0 && event.timeStamp - lastWheelUpTime > 50) { lastWheelUpTime = event.timeStamp; // WheelUp downKeyCBMap.get("WheelUp")?.(); upKeyCBMap.get("WheelUp")?.(); } } function addShortcut( key: string, downCB?: () => Promise, loopCB?: () => Promise, upCB?: () => Promise, cancelAble = false // only work with downCB && upCB ) { downKeyMap.set(key, false); if (cancelAble && downCB && upCB) { cancelAbleKeyList.push(key); const cancelAbleUpCB = async () => { loopDownKeyCBMap.delete(key); upKeyCBMap.delete(key); await upCB(); }; if (loopCB) downKeyCBMap.set(key, async () => { loopDownKeyCBMap.set(key, loopCB); upKeyCBMap.set(key, cancelAbleUpCB); await downCB(); }); else { // no loopCB downKeyCBMap.set(key, async () => { upKeyCBMap.set(key, cancelAbleUpCB); await downCB(); }); } } else { if (downCB && loopCB) downKeyCBMap.set(key, async () => { downCB(); loopDownKeyCBMap.set(key, loopCB); }); else if (downCB) { // no loopCB downKeyCBMap.set(key, downCB); } if (upCB) { upKeyCBMap.set(key, async () => { loopDownKeyCBMap.delete(key); upCB(); }); } } } /** * execute the json object macro * @param macro * @example * await execMacro([ * // touch down * { * type: "touch", * // op, pointerId, posX, posY * args: ["down", 5, ["mouse", -10], 600], * }, * // sleep 1000ms * { * type: "sleep", * // time(ms) * args: [1000], * }, * // touch up * { * type: "touch", * args: ["up", 5, ["mouse", 10], 600], * }, * // touch 1000ms * { * type: "touch", * args: ["default", 5, ["mouse", 10], 600, 1000], * }, * // swipe * { * type: "swipe", * // op, pointerId, posList, intervalBetweenPos * args: [ * "default", 5, * [ * [ * ["mouse", 100], * ["mouse", -100], * ], * ["mouse", "mouse"], * ], * 1000, * ], * }, * // input-text * { * type: "input-text", * // 1:on, 2:off * args: [1] * } * ]); */ async function execMacro( relativeSize: { w: number; h: number }, macro: KeyMacroList ) { if (macro === null) return; for (const cmd of macro) { if (!cmd.hasOwnProperty("type") || !cmd.hasOwnProperty("args")) { console.error("Invalid command: ", cmd); return; } try { switch (cmd.type) { case "sleep": await sleep(cmd.args[0]); break; case "touch": let touchAction; switch (cmd.args[0]) { case "default": touchAction = TouchAction.Default; break; case "down": touchAction = TouchAction.Down; break; case "up": touchAction = TouchAction.Up; break; case "move": touchAction = TouchAction.Move; break; default: console.error("Invalid touch action: ", cmd.args[0]); return; } await touchX( touchAction, cmd.args[1], calculateMacroPosX(cmd.args[2], relativeSize.w), calculateMacroPosY(cmd.args[3], relativeSize.h), cmd.args.length > 4 ? cmd.args[4] : undefined ); break; case "swipe": let swipeAction; switch (cmd.args[0]) { case "default": swipeAction = SwipeAction.Default; break; case "noUp": swipeAction = SwipeAction.NoUp; break; case "noDown": swipeAction = SwipeAction.NoDown; break; default: console.error("Invalid swipe action: ", cmd.args[0]); return; } await swipe({ action: swipeAction, pointerId: cmd.args[1], screen: { w: screenSizeW, h: screenSizeH, }, pos: calculateMacroPosList(cmd.args[2], relativeSize), intervalBetweenPos: cmd.args[3], }); break; case "input-text": if (cmd.args[0] === 1) { // on useGlobalStore().showInputBox(true); } else { // off useGlobalStore().showInputBox(false); } break; default: console.error("Invalid command: ", cmd); return; } } catch (e) { console.error("Invalid command: ", cmd, e); return; } } } let loopFlag = false; function execLoopCB() { loopDownKeyCBMap.forEach((cb) => { cb(); }); if (loopFlag) requestAnimationFrame(execLoopCB); } // change ts type function asType(_val: any): asserts _val is T {} function applyKeyMappingConfigShortcuts( keyMappingConfig: KeyMappingConfig ): boolean { try { const relativeSize = keyMappingConfig.relativeSize; for (const item of keyMappingConfig.list) { switch (item.type) { case "SteeringWheel": asType(item); addSteeringWheelKeyboardShortcuts( item.key, relativeSize, item.posX, item.posY, item.offset, item.pointerId ); break; case "DirectionalSkill": asType(item); addDirectionalSkillShortcuts( item.key, relativeSize, item.posX, item.posY, item.range, item.pointerId ); break; case "DirectionlessSkill": asType(item); addDirectionlessSkillShortcuts( item.key, relativeSize, item.posX, item.posY, item.pointerId ); break; case "CancelSkill": asType(item); addCancelSkillShortcuts( item.key, relativeSize, item.posX, item.posY, item.pointerId ); break; case "Tap": asType(item); addTapShortcuts( item.key, relativeSize, item.time, item.posX, item.posY, item.pointerId ); break; case "TriggerWhenPressedSkill": asType(item); addTriggerWhenPressedSkillShortcuts( item.key, relativeSize, item.posX, item.posY, item.directional, item.rangeOrTime, item.pointerId ); break; case "TriggerWhenDoublePressedSkill": asType(item); addTriggerWhenDoublePressedSkillShortcuts( item.key, relativeSize, item.posX, item.posY, item.range, item.pointerId ); break; case "Observation": asType(item); addObservationShortcuts( item.key, relativeSize, item.posX, item.posY, item.scale, item.pointerId ); break; case "Macro": asType(item); addShortcut( item.key, item.macro.down === null ? undefined : async () => { await execMacro(relativeSize, item.macro.down); }, item.macro.loop === null ? undefined : async () => { await execMacro(relativeSize, item.macro.loop); }, item.macro.up === null ? undefined : async () => { await execMacro(relativeSize, item.macro.up); } ); break; default: console.error("Invalid item type: ", item); break; } } return true; } catch (e) { console.error("Invalid keyMappingConfig: ", keyMappingConfig, e); clearShortcuts(); return false; } } async function touchX( action: TouchAction, pointerId: number, posX: number, posY: number, time?: number ) { await touch({ action, pointerId, screen: { w: screenSizeW, h: screenSizeH, }, pos: { x: posX, y: posY, }, time, }); } export function listenToEvent() { window.addEventListener("keydown", handleKeydown); window.addEventListener("keyup", handleKeyup); window.addEventListener("mousedown", handleMouseDown); window.addEventListener("mousemove", handleMouseMove); window.addEventListener("mouseup", handleMouseUp); window.addEventListener("wheel", handleMouseWheel); loopFlag = true; execLoopCB(); } export function unlistenToEvent() { window.removeEventListener("keydown", handleKeydown); window.removeEventListener("keyup", handleKeyup); window.removeEventListener("mousedown", handleMouseDown); window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("mouseup", handleMouseUp); window.removeEventListener("wheel", handleMouseWheel); loopFlag = false; } export function clearShortcuts() { downKeyMap.clear(); downKeyCBMap.clear(); loopDownKeyCBMap.clear(); upKeyCBMap.clear(); cancelAbleKeyList.length = 0; } export function updateScreenSizeAndMaskArea( screenSize: [number, number], maskArea: [number, number] ) { screenSizeW = screenSize[0]; screenSizeH = screenSize[1]; maskSizeW = maskArea[0]; maskSizeH = maskArea[1]; } export function applyShortcuts( element: HTMLElement, keyMappingConfig: KeyMappingConfig, messageAPI: ReturnType ) { maskElement = element; message = messageAPI; addClickShortcuts("M0", 0); const relativeSize = { w: 1280, h: 720 }; const sightKeyMapping = { type: "Sight" as "Sight", key: "KeyH", pointerId: 0, note: "准星键", posX: 640, posY: 360, scaleX: 1, scaleY: 1, }; const fireKeyMapping = { type: "Fire" as "Fire", pointerId: 2, note: "开火键", posX: 300, posY: 300, drag: true, scaleX: 0.5, scaleY: 0.2, }; addSightShortcuts(relativeSize, sightKeyMapping, fireKeyMapping); return applyKeyMappingConfigShortcuts(keyMappingConfig); }