#Requires AutoHotkey v1.1.33+ #NoEnv #SingleInstance Force #UseHook #InstallKeybdHook #InstallMouseHook SendMode Input SetBatchLines, -1 #MaxThreadsPerHotkey 1 ; ========================= ; 옵션 ; ========================= AUTO_RUN_ALL := 1 ; 1=PgUp 한번에 목록 끝까지 연속 실행, 0=PgUp 누를 때마다 1개만 RESET_TO_START_WHEN_DONE := 1 ; 1=목록 끝나면 다음 실행 대비 1로 리셋 GAP := 1000 ; 단계 간 1초 GAP_BETWEEN_ITEMS := 1200 ; 아이템 사이클 간격 ; ========================= ; 아이템 ID 리스트 ; ========================= global ITEM_IDS := [] ; 실제 아이템 ID는 회사 내부 데이터이므로 전량 제거했습니다. ; 아래는 공개용 샘플 ID입니다. 실제 사용 시 검수 대상 ID 목록으로 교체하세요. Loop, 150 ITEM_IDS.Push(Format("SAMPLE_ITEM_{:03}", A_Index)) ; ========================= ; 런타임 변수/좌표 ; ========================= global RUNNING := false global STOP := false global STEP := 0 global curIdx := 1 global curItemID := "" global x1 := "", y1 := "" ; Ctrl+Alt+1 : 장착 더블클릭(아이템칸) global x2 := "", y2 := "" ; Ctrl+Alt+2 : 인벤 닫기(클릭) global x3 := "", y3 := "" ; Ctrl+Alt+3 : 몬스터 타겟(공격 클릭) global x4 := "", y4 := "" ; Ctrl+Alt+4 : 공격 끊기(바닥 클릭) global x5 := "", y5 := "" ; Ctrl+Alt+Q : UI 인벤 버튼 global x6 := "", y6 := "" ; Ctrl+Alt+W : 해제 더블클릭(아이템칸) global x7 := "", y7 := "" ; Ctrl+Alt+E : 예비 global x8 := "", y8 := "" ; Ctrl+Alt+R : 예비 ; ========================= ; 타겟 클라이언트 정보 ; ========================= global TARGET_HWND := "" global TARGET_TITLE := "" global TARGET_EXE := "" CoordMode, Mouse, Screen return ; <-- auto-execute 끝 ; ========================= ; 유틸 ; ========================= Tip(msg, ms := 900) { ToolTip, %msg% SetTimer, __hideTip, -%ms% } __hideTip: ToolTip return EnterDU() { SendInput, {Enter down} Sleep, 30 SendInput, {Enter up} Sleep, 50 } F12DU() { SendInput, {F12 down} Sleep, 50 SendInput, {F12 up} Sleep, 150 } LClickDU() { SendInput, {LButton down} Sleep, 30 SendInput, {LButton up} Sleep, 50 } LDblClickDU() { LClickDU() Sleep, 200 LClickDU() } ShiftInsertDU() { SendInput, {Shift down} Sleep, 20 SendInput, {Insert down} Sleep, 30 SendInput, {Insert up} Sleep, 20 SendInput, {Shift up} Sleep, 80 } PasteByShiftInsert(txt) { old := Clipboard Clipboard := "" Sleep, 30 Clipboard := txt ClipWait, 1 Sleep, 150 ShiftInsertDU() Sleep, 150 Clipboard := old } GetMaxIdx() { global ITEM_IDS return ITEM_IDS.MaxIndex() } SetCurrentItem() { global ITEM_IDS, curIdx, curItemID max := ITEM_IDS.MaxIndex() if (!max) { curItemID := "" return false } if (curIdx > max) curIdx := 1 curItemID := ITEM_IDS[curIdx] return true } AdvanceItem() { global curIdx curIdx := curIdx + 1 } SetTargetClient() { global TARGET_HWND, TARGET_TITLE, TARGET_EXE WinGet, hwnd, ID, A if (!hwnd) return false WinGetTitle, title, ahk_id %hwnd% WinGet, exe, ProcessName, ahk_id %hwnd% TARGET_HWND := hwnd TARGET_TITLE := title TARGET_EXE := exe return true } EnsureTargetWindow() { global TARGET_HWND, TARGET_TITLE, TARGET_EXE if (!TARGET_HWND) return false if !WinExist("ahk_id " . TARGET_HWND) return false WinGet, mm, MinMax, ahk_id %TARGET_HWND% if (mm = -1) WinRestore, ahk_id %TARGET_HWND% WinActivate, ahk_id %TARGET_HWND% WinWaitActive, ahk_id %TARGET_HWND%,, 1 if !WinActive("ahk_id " . TARGET_HWND) return false return true } AbortRun(msg := "중지됨") { global RUNNING, STOP, STEP RUNNING := false STOP := true STEP := 0 SetTimer, __Runner, Off Tip(msg, 1400) } ; ========================= ; 좌표 캡처 ; ========================= ^!1:: MouseGetPos, x1, y1 Tip("[1] 장착 더블클릭: " . x1 . ", " . y1, 1200) return ^!2:: MouseGetPos, x2, y2 Tip("[2] 인벤 닫기: " . x2 . ", " . y2, 1200) return ^!3:: MouseGetPos, x3, y3 Tip("[3] 몬스터 타겟: " . x3 . ", " . y3, 1200) return ^!4:: MouseGetPos, x4, y4 Tip("[4] 공격 끊기: " . x4 . ", " . y4, 1200) return ^!q:: MouseGetPos, x5, y5 Tip("[5=Q] UI 인벤 버튼: " . x5 . ", " . y5, 1200) return ^!w:: MouseGetPos, x6, y6 Tip("[6=W] 해제 더블클릭: " . x6 . ", " . y6, 1200) return ^!e:: MouseGetPos, x7, y7 Tip("[7=E] 예비: " . x7 . ", " . y7, 1200) return ^!r:: MouseGetPos, x8, y8 Tip("[8=R] 예비: " . x8 . ", " . y8, 1200) return ; ========================= ; 타겟 클라이언트 지정 ; ========================= ^!0:: if (SetTargetClient()) Tip("[클라이언트 지정] " . TARGET_EXE . " / " . TARGET_TITLE, 1800) else Tip("클라이언트 지정 실패", 1200) return ; ========================= ; 인덱스 리셋 ; ========================= F9:: curIdx := 1 if (SetCurrentItem()) Tip("인덱스 리셋 -> ID: " . curItemID, 1200) else Tip("ITEM_IDS 비어있음", 1200) return ; ========================= ; 중지/시작 ; ========================= PgDn:: STOP := true RUNNING := false STEP := 0 SetTimer, __Runner, Off Tip("[STOP] 중지됨", 900) return *PgUp:: if (RUNNING) { Tip("이미 실행 중", 800) return } if (TARGET_HWND = "") { Tip("먼저 Ctrl+Alt+0 으로 대상 클라이언트를 지정하세요", 1800) return } if (!EnsureTargetWindow()) { Tip("지정된 클라이언트를 찾을 수 없거나 활성화 실패", 1800) return } if (x1="" || x2="" || x3="" || x4="" || x5="" || x6="") { Tip("좌표 비어있음! Ctrl+Alt+1~4, Ctrl+Alt+Q/W 로 1~6 캡처 후 PgUp", 1800) return } if (!SetCurrentItem()) { Tip("ITEM_IDS 비어있음", 1400) return } STOP := false RUNNING := true STEP := 1 max := GetMaxIdx() Tip("[RUN] (" . curIdx . "/" . max . ") ID: " . curItemID, 1200) SetTimer, __Runner, -10 return ; ========================= ; 메인 로직 ; ========================= __Runner: if (!RUNNING || STOP) { SetTimer, __Runner, Off return } if (!EnsureTargetWindow()) { AbortRun("지정 클라이언트 활성화 실패") return } ; 1) 엔터(채팅창 열림) if (STEP = 1) { EnterDU() STEP := 2 SetTimer, __Runner, -%GAP% return } ; 2) @아이템추가 curItemID if (STEP = 2) { PasteByShiftInsert("@아이템추가 " . curItemID) STEP := 3 SetTimer, __Runner, -%GAP% return } ; 3) 엔터(입력) if (STEP = 3) { EnterDU() STEP := 4 SetTimer, __Runner, -%GAP% return } ; 4) 엔터(채팅 닫기) if (STEP = 4) { EnterDU() STEP := 5 SetTimer, __Runner, -%GAP% return } ; 5) 인벤 아이템칸 이동 if (STEP = 5) { MouseMove, %x1%, %y1%, 0 STEP := 6 SetTimer, __Runner, -%GAP% return } ; 6) 더블클릭(장착) if (STEP = 6) { LDblClickDU() STEP := 7 SetTimer, __Runner, -%GAP% return } ; 7) 인벤 닫기 이동 if (STEP = 7) { MouseMove, %x2%, %y2%, 0 STEP := 8 SetTimer, __Runner, -%GAP% return } ; 8) 클릭(인벤 닫음) if (STEP = 8) { LClickDU() STEP := 9 SetTimer, __Runner, -%GAP% return } ; 9) 엔터(채팅 열림) if (STEP = 9) { EnterDU() STEP := 10 SetTimer, __Runner, -%GAP% return } ; 10) !! if (STEP = 10) { PasteByShiftInsert("!!") STEP := 11 SetTimer, __Runner, -%GAP% return } ; 11) 엔터(입력) if (STEP = 11) { EnterDU() STEP := 12 SetTimer, __Runner, -%GAP% return } ; 12) 엔터(채팅 닫기) if (STEP = 12) { EnterDU() STEP := 13 SetTimer, __Runner, -%GAP% return } ; 13) 몬스터 이동 if (STEP = 13) { MouseMove, %x3%, %y3%, 0 STEP := 14 SetTimer, __Runner, -%GAP% return } ; 14~16) 공격 클릭 3회 if (STEP = 14) { LClickDU() STEP := 15 SetTimer, __Runner, -%GAP% return } if (STEP = 15) { LClickDU() STEP := 16 SetTimer, __Runner, -%GAP% return } if (STEP = 16) { LClickDU() STEP := 17 SetTimer, __Runner, -%GAP% return } ; 17) 공격 끊기 이동 if (STEP = 17) { MouseMove, %x4%, %y4%, 0 STEP := 18 SetTimer, __Runner, -%GAP% return } ; 18) 클릭(공격 끊기) if (STEP = 18) { LClickDU() STEP := 19 SetTimer, __Runner, -%GAP% return } ; 19) UI 인벤 버튼 이동 if (STEP = 19) { MouseMove, %x5%, %y5%, 0 STEP := 20 SetTimer, __Runner, -%GAP% return } ; 20) 클릭(인벤 팝업) if (STEP = 20) { LClickDU() STEP := 21 SetTimer, __Runner, -%GAP% return } ; 21) 해제 아이템칸 이동 if (STEP = 21) { MouseMove, %x6%, %y6%, 0 STEP := 22 SetTimer, __Runner, -%GAP% return } ; 22) 더블클릭(해제) if (STEP = 22) { LDblClickDU() STEP := 23 SetTimer, __Runner, -%GAP% return } ; 23) 엔터(채팅 열기) if (STEP = 23) { EnterDU() STEP := 24 SetTimer, __Runner, -%GAP% return } ; 24) @인벤청소 if (STEP = 24) { PasteByShiftInsert("@인벤청소") STEP := 25 SetTimer, __Runner, -%GAP% return } ; 25) 엔터(입력) if (STEP = 25) { EnterDU() STEP := 26 SetTimer, __Runner, -%GAP% return } ; 26) 엔터(채팅 닫기) if (STEP = 26) { EnterDU() STEP := 27 SetTimer, __Runner, -%GAP% return } ; 27) F12 스크린샷 if (STEP = 27) { F12DU() AdvanceItem() max := GetMaxIdx() if (AUTO_RUN_ALL && curIdx <= max && !STOP) { SetCurrentItem() STEP := 1 Tip("[NEXT] (" . curIdx . "/" . max . ") ID: " . curItemID, 1200) SetTimer, __Runner, -%GAP_BETWEEN_ITEMS% return } else { RUNNING := false STOP := false STEP := 0 SetTimer, __Runner, Off if (curIdx > max) { Tip("완료! (목록 끝)", 1200) if (RESET_TO_START_WHEN_DONE) curIdx := 1 } else { Tip("완료! 다음 ID 준비: " . ITEM_IDS[curIdx], 1400) } return } } return