|
2025-12-16 02:36
조회: 21,093
추천: 77
눈사람 구출 작전 자동화 스크립트 공유합니다.직접 클릭하다가 현타와서 ai로 스크립트 만들어봤습니다. 크롬 사용하셔야되고 크롬 개발자도구(F12)의 console 탭에서 사용하시면 됩니다. 많은 사람들이 볼 수 있게 추천좀 해주시면 감사하겠습니다. 제발 이런 이벤트 좀 안만들었으면 좋겠네요 ![]() 요기 아래 스크립트 복사해서 이벤트 페이지에서 사용하시면 됩니다. ===================요기 아래가 스크립트===================================== (() => { // ========================================================= // Options // ========================================================= const OPT = { stageSelector: "section.stage", floorSelector: '[class^="floor"]', doorSelector: "button.door", // Timing initialWaitMs: 1000, // "게임 시작" 후 DOM 안정화 대기 clickDelayMs: 40, // 클릭 후 "층 변화"가 나타날 때까지 기다리는 최대 시간 waitFloorChangeMaxMs: 1600, waitFloorChangeTickMs: 80, // 루프 대기 loopTickMs: 80, // STAGE CLEAR 자동 확인 클릭(대개 reload됨) autoConfirmStageClear: false, modalBodySelector: ".lui-modal__body", modalTitleSelector: ".lui-modal__body .lui-modal__title", modalConfirmSelector: ".lui-modal__body button.lui-modal__confirm", }; // ========================================================= // Utils // ========================================================= const sleep = (ms) => new Promise(r => setTimeout(r, ms)); const qs = (s) => document.querySelector(s); const qsa = (s, r=document) => Array.from(r.querySelectorAll(s)); const log = (...a) => console.log("[auto-door]", ...a); const stage = qs(OPT.stageSelector); if (!stage) { log("stage not found"); return; } const floors = () => qsa(OPT.floorSelector, stage); const getDoorElByIdx = (idx) => stage.querySelector(`${OPT.doorSelector}[data-idx="${idx}"]`); const getCurrentFloorIndex = () => { const fs = floors(); for (let i = 0; i < fs.length; i++) { if (fs[i].querySelector(".door--current")) return i; } return null; }; // "1층으로 돌아옴" 판정: floor1 안의 문들이 전부 door--current가 됨 (네가 준 조건) const isReturnedToFloor1 = () => { const fs = floors(); const f0 = fs[0]; if (!f0) return false; const doors = qsa(OPT.doorSelector, f0); if (!doors.length) return false; return doors.every(d => d.classList.contains("door--current")); }; const enabledDoorIdxListOnFloor = (floorIdx) => { const fs = floors(); const f = fs[floorIdx]; if (!f) return []; return qsa(`${OPT.doorSelector}:not(:disabled)`, f) .map(d => Number(d.dataset.idx)) .filter(Number.isInteger); }; // STAGE CLEAR 모달 감지 const isStageClearModal = () => { const body = qs(OPT.modalBodySelector); if (!body) return false; const title = (qs(OPT.modalTitleSelector)?.textContent || "").trim(); return title === "STAGE CLEAR"; }; // 클릭 후 floorIndex 변화 감지(최대 waitFloorChangeMaxMs까지 polling) const waitFloorChange = async (prevFloor) => { const t0 = performance.now(); while (performance.now() - t0 < OPT.waitFloorChangeMaxMs) { const cur = getCurrentFloorIndex(); if (cur !== null && cur !== prevFloor) return cur; await sleep(OPT.waitFloorChangeTickMs); } // 변화가 없으면 prevFloor 그대로 반환 return prevFloor; }; // ========================================================= // Build NxM (floor count varies) // ========================================================= const buildBoard = () => { const fs = floors(); const board = fs.map((floorEl, f) => qsa(OPT.doorSelector, floorEl).map(d => Number(d.dataset.idx)).filter(Number.isInteger) ); return { N: board.length, board }; }; // ========================================================= // Main // ========================================================= (async () => { log("start"); await sleep(OPT.initialWaitMs); const { N, board } = buildBoard(); log(`parsed floors=${N}, matrix=`, board); // floor -> correct idx (unknown = null) const correctByFloor = Array(N).fill(null); // floor -> tried idx set const triedByFloor = Array.from({ length: N }, () => new Set()); // 1층부터 연속으로 알고 있는 정답만 복구 (중간에 모르면 stop) const restorePath = async () => { for (let f = 0; f < N; f++) { const idx = correctByFloor[f]; if (!Number.isInteger(idx)) return; // 현재 층이 f가 아닐 수 있으니 확인 const cur = getCurrentFloorIndex(); if (cur !== f) return; const el = getDoorElByIdx(idx); if (!el || el.matches(":disabled")) return; log(`restore: floor ${f + 1} idx ${idx}`); await sleep(OPT.clickDelayMs); el.click(); // 정답 복구 클릭은 보통 바로 위층으로 올라가야 함 await waitFloorChange(f); await sleep(OPT.clickDelayMs); } }; // 아직 모르는 층에서 "안 눌러본 문" 선택 const pickUntriedIdx = (floorIdx) => { const candidates = enabledDoorIdxListOnFloor(floorIdx); const tried = triedByFloor[floorIdx]; for (const idx of candidates) { if (!tried.has(idx)) return idx; } return null; }; while (true) { // Stage clear if (isStageClearModal()) { log("STAGE CLEAR modal detected"); if (OPT.autoConfirmStageClear) qs(OPT.modalConfirmSelector)?.click(); break; } const curFloor = getCurrentFloorIndex(); if (curFloor === null) { await sleep(OPT.loopTickMs); continue; } // 1층으로 돌아왔고, 1층 정답을 알고 있으면 -> 연속 복구 if (curFloor === 0 && isReturnedToFloor1() && Number.isInteger(correctByFloor[0])) { log("returned to floor1 -> restore known path"); await restorePath(); await sleep(OPT.loopTickMs); continue; } // 현재 층 정답을 알고 있으면 그걸 클릭(복구/진행) if (Number.isInteger(correctByFloor[curFloor])) { const idx = correctByFloor[curFloor]; const el = getDoorElByIdx(idx); if (el && !el.matches(":disabled")) { log(`known: floor ${curFloor + 1} idx ${idx}`); await sleep(OPT.clickDelayMs); el.click(); const newFloor = await waitFloorChange(curFloor); // newFloor가 안 변해도 계속 루프 await sleep(OPT.loopTickMs); continue; } } // 탐색: 아직 모르는 층이면 안 눌러본 문을 클릭 const cand = pickUntriedIdx(curFloor); if (!Number.isInteger(cand)) { // 현재 층 문을 다 눌렀다는 뜻인데, 보통 여기까지 오면 로직 어긋난 것. // 안전하게 tried 초기화하고 다시 시도. triedByFloor[curFloor].clear(); await sleep(OPT.loopTickMs); continue; } triedByFloor[curFloor].add(cand); const candEl = getDoorElByIdx(cand); if (!candEl || candEl.matches(":disabled")) { await sleep(OPT.loopTickMs); continue; } log(`explore: floor ${curFloor + 1} idx ${cand}`); await sleep(OPT.clickDelayMs); candEl.click(); const newFloor = await waitFloorChange(curFloor); // ✅ 정답 판정은 "층이 올라갔는지"로 확정 if (newFloor === curFloor + 1) { correctByFloor[curFloor] = cand; log(`CONFIRMED: floor ${curFloor + 1} correct idx=${cand}`); } else { log(`FAIL: floor ${curFloor + 1} idx=${cand} -> newFloor=${newFloor + 1}`); } await sleep(OPT.loopTickMs); } log("end"); })(); })(); |
로스트아크 인벤 자유 게시판 게시판
인벤 전광판
[전국절제협회] 사멸의 왕, 절제가 하늘에 서겠다.
[계곡능이백숙] 슈차잔재 상향점여
[갓래스터] 아만서버 길드는 길참잘! 신규 길드원 대 모집!
[Asadas1231] 식장 잡았습니다 퍼리만 주십시오
[강시엘] 기상이리메이크시급한데좀살려주세요딸깍으로안되는거알면서
[수인사람] 퍼리와 결혼해라
로아 인벤 전광판 시작!!
[계란후라이찜] ㅇㅎ는 빨리 게시글을 누르라는 말입니다.
[혁륜조] 슬레 상향좀 해주세요
[수인사람] 퍼리가..이세상에..존재하지 않는다?








