4.3.0

; -----------------------------------------------------------------------------------------
; Clicker Heroes Sw1ft Bot
; by Sw1ftb
; -----------------------------------------------------------------------------------------

; #Warn All
#Persistent
#NoEnv
#InstallKeybdHook

#Include %A_ScriptDir%
#Include ch_bot_lib.ahk

SetControlDelay, -1

scriptName=CH Sw1ft Bot
scriptVersion=4.3.0
minLibVersion=4.0

script := scriptName . " v" . scriptVersion

clickerScript := "monster_clicker.ahk"
clickerName := clickerScript . " - AutoHotkey"

scheduleReload := false
scheduleStop := false
manualProgression := false

elapsedTime := 0.0

; -----------------------------------------------------------------------------------------

#Include system\wm_messages.ahk

; Load system default settings
#Include system\ch_bot_default_settings.ahk

IfNotExist, ch_bot_settings.ahk
{
	FileCopy, system\ch_bot_default_settings.ahk, ch_bot_settings.ahk
}

; Load user settings
#Include *i ch_bot_settings.ahk

if (libVersion != minLibVersion) {
	showWarningSplash("The bot lib version must be " . minLibVersion . "!")
	ExitApp
}

SysGet, VirtualScreenWidth, 78
SysGet, VirtualScreenHeight, 79

logVariable("A_AhkVersion", A_AhkVersion)
logVariable("A_OSVersion", A_OSVersion)
logVariable("A_ScreenWidth", A_ScreenWidth)
logVariable("A_ScreenHeight", A_ScreenHeight)
if (VirtualScreenWidth > A_ScreenHeight) {
	logVariable("VirtualScreenWidth", VirtualScreenWidth)
	logVariable("VirtualScreenHeight", VirtualScreenHeight)
}
logVariable("A_ScreenDPI", A_ScreenDPI )
logVariable("script", script)

clientCheck()

Run, "%A_ScriptDir%\%clickerScript%",, UseErrorLevel
if (ErrorLevel != 0) {
	playWarningSound()
	msgbox,,% script,% "Failed to auto-start " . clickerScript . " (system error code = " . A_LastError . ")!"
}

handleAutorun()

; -----------------------------------------------------------------------------------------
; -- Hotkeys (+=Shift, !=Alt, ^=Ctrl, #=Win)
; -----------------------------------------------------------------------------------------

; Show the cursor position with Alt+Middle Mouse Button
!mbutton::
	mousegetpos, xpos, ypos
	msgbox,,% script,% "Cursor position: x" xpos-leftMarginOffset " y" ypos-topMarginOffset
return

; Top margin calculation when clicking in the center of the ancient tab eye
^mbutton::
	mousegetpos, xpos, ypos
	msgbox,,% script,% "Set browserTopMargin to: " . ypos-102
return

; -- Main Hotkeys -------------------------------------------------------------------------

; Start a Vision Run loop with Ctrl+F1
^F1::
	loopVisionRun()
return

; Reload and restart Vision Run loop with Ctrl+F3
^F3::
	global scheduleReload := true
	handleScheduledReload("loopVisionRun")
return

; Pause/unpause the script
~Pause::Pause
return

; Abort any active run or initiated ascension with Alt+Pause
!Pause::
	showUserSplash("Aborting...")
	exitThread := true
return
Hotkey, !Pause, , P10

; Schedule a stop after finishing the current run with Shift+Pause
+Pause::
	toggleFlag("scheduleStop", scheduleStop)
return

; Reload the script (needed after configuration changes)
!F5::
	global scheduleReload := true
	handleScheduledReload()
return
Hotkey, !F5, , P10

; Schedule a script reload after finishing the current run, then restart it
+^F5::
	toggleFlag("scheduleReload", scheduleReload)
return

; Re-initialize coordinates (needed after moving or re-sizing the client window)
!F6::
	showUserSplash("Re-initialize coordinates... ")
	clientCheck()
	clickerInitialize()
return
Hotkey, !F6, , P10

; -- Supplementary Hotkeys ----------------------------------------------------------------

; Suspend/Unsuspend all other Hotkeys with Ctrl+Esc
~^Esc::Suspend, Toggle
return

; Autosave the game
^F11::
	save()
return
Hotkey, ^F11, , P5

; Raid once for free with Win+F6
#F6::
	raid()
return
Hotkey, #F6, , P5

; One paid raid
#F7::
	raid(1)
return
Hotkey, #F7, , P5

; Paid raids
#F8::
	raid(1, raidAttempts)
return
Hotkey, #F8, , P5

; Toggle boolean (true/false) flags with Shift+Ctrl+Fx

+^F1::
	toggleFlag("autoAscend", autoAscend)
return

+^F6::
	toggleFlag("playNotificationSounds", playNotificationSounds)
return
Hotkey, +^F6, , P5

+^F7::
	toggleFlag("playWarningSounds", playWarningSounds)
return

+^F11::
	toggleFlag("saveBeforeAscending", saveBeforeAscending)
return

; -- Test Hotkeys -------------------------------------------------------------------------

; Ctrl+Alt+F1 should scroll down to the bottom, then back up
^!F1::
	scrollToBottom()
	scrollToTop()
return

; Ctrl+Alt+F2 should switch between all used tabs
^!F2::
	switchToAncientTab()
	switchToRelicTab()
	switchToClanTab()
	switchToCombatTab()
return

; -----------------------------------------------------------------------------------------
; -- Functions
; -----------------------------------------------------------------------------------------

getClickable(active:=1) {
	global
	local xPos, yPos
	if (active = 1) {
		clickPos(524, 487)
		clickPos(747, 431)
		clickPos(760, 380)
		clickPos(873, 512)
		clickPos(1005, 453)
		clickPos(1053, 443)
	} else {
		loop % clickableImageFiles.Length()
		{
			imgClickable.file := clickableImageFiles[A_Index]
			if (locateImage(imgClickable, xPos, yPos)) {
				clickPos(xPos, yPos, 1, 1) ; absolute pos
				break
			}
		}
	}
}

; Level up and upgrade all heroes
initRun(initMode:=0) {
	global
	local hasAscendButton := false
	local hasSkillBar := false
	local hasNoSkillLocked := false

	showDebugSplash("Init Run @ Lvl " . getCurrentZone())

	switchToCombatTab()
	scrollToTop()
	reFocus()

	local foundDK := false
	local xButton, yButton, xDK, yDK, x
	local clickCount := initMode = 1 ? 1 : 2

	locateImage(imgCoin, xButton, yButton) ; get a x coordinate for the hire/lvl up buttons

	loop 10 ; pages
	{
		if (locateImage(imgDK, xDK, yDK) or locateImage(imgDKG, xDK, yDK)) {
			foundDK := true
		}
		loop 5 ; attempts per page
		{
			if (locateImage(imgDimmedSkill, x, yButton)
					or locateImage(imgHire, x, yButton)) {
				if (foundDK and yButton > yDK) {
					; Don't level anything below Frostleaf
					continue
				}
				ctrlClick(xButton, yButton, clickCount, 1, 1)
			} else {
				break
			}
		}
		if (foundDK) {
			if (locateImage(imgAscend)) {
				hasAscendButton := true
			}
			break
		}
		scrollDown(5)
	}
	scrollToBottom()
	buyAvailableUpgrades()

	if (initMode = 0) {
		if (locateImage(imgSkillBar)) {
			hasSkillBar := true
		}
		if (!locateImage(imgSkillLocked)) {
			hasNoSkillLocked := true
		}
		logVariable("hasAscendButton", hasAscendButton, true, "TRACE")
		logVariable("hasSkillBar", hasSkillBar, true, "TRACE")
		logVariable("hasNoSkillLocked", hasNoSkillLocked, true, "TRACE")
	}

	return hasAscendButton and hasSkillBar and hasNoSkillLocked
}

getEndZone() {
	global
	return endLvlActive > endLvlIdle ? endLvlActive : endLvlIdle
}

getState() {
	global

	if (!WinExist(winName)) {
		return -2 ; no ch window
	}
	if (getCurrentZone() = 0) {
		return -1 ; vision, but not in browser
	}
	if (!locateImage(imgSmile)) {
		return 0 ; vision, but not finding anything
	}
	if (getCurrentZone() < getEndZone() and !readyToAscend) {
		return 1 ; ready for progression
	} else {
		return 2 ; ready to ascend
	}
}

loopVisionRun() {
	global
	readyToAscend := false

	local state := 0

	showUserSplash("Starting Vision Runs!")

	logVariable("browser", browser)
	logVariable("browserTopMargin", browserTopMargin)
	logVariable("gildedRanger", rangers[gildedRanger])
	logVariable("endLvlIdle", endLvlIdle)
	logVariable("endLvlActive", endLvlActive)
	logVariable("maxIdleMonsterKillTime", maxIdleMonsterKillTime)
	logVariable("maxMonsterKillTime", maxMonsterKillTime)
	logVariable("chronos", chronos)
	logVariable("kumawakamaru", kumawakamaru)
	logVariable("vaagur", vaagur)
	if (endLvlActive > 0) {
		logArray("skillCombo", skillCombo)
	}
	logVariable("clickableHuntDelay", clickableHuntDelay)
	logVariable("saveBeforeAscending", saveBeforeAscending, true)
	logVariable("autoAscend", autoAscend, true)
	logVariable("noSleep", noSleep, true)
	logVariable("levelSolomon", levelSolomon, true)

	loop
	{
		state := getState()
		if (state < 1) {
			showWarningSplash("Start failed (state = " . state . ")! Trying again...")
			clientCheck()
			clickerInitialize()
			continue
		}
		if (getState() = 1) {
			visionRun()
		}
		if (getState() = 2) {
			if (saveBeforeAscending) {
				save()
			}
			ascend(autoAscend)
			readyToAscend := false
			if (levelSolomon) {
				solomonLeveler(solomonLevels)
			}
			handleScheduledStop()
			handleScheduledReload("loopVisionRun")
		}
	}
}

visionRun() {
	global
	exitThread := false
	isResuming := false

	local startRunTime := A_TickCount

	local isInitiated := false
	local isClickerRunning := false
	local isComboActive := false
	local triggerActive := false
	local hasBomberBuff := gildedRanger = 12 ? true : false
	local hasGogBuff := gildedRanger = 13 ? true : false
	local foundTheHero := false

	local xBtn := 0, yBtn := 0, isNew := 0, startAt := 0
	local xSkill := 0, ySkill := 0, skillSearch := false

	local locateGildedDelay := gildedRanger ? 60 : 4
	local locateBuyUpgradesDelay := 20
	local progressCheckDelay := 10

	local t := 0
	local elapsedRunTime := 0

	local comboDelay := skillCombo[1]
	comboIndex := 2

	manualProgression := false
	switchToCombatTab()
	setProgressionMode()
	if (!locateImage(imgProgression)) {
		showTraceSplash("Manual progression: On")
		manualProgression := true
		SetTimer, nextZoneTimer, 500
	}

	local previousZone := 0
	local previousIdleZone := 0

	local zoneMonsters := 10.0 + kumawakamaru
	local secPerMonster := 0
	local farmMonsterKillTime := 2.0 ; stop and farm before boss when exceeded

	farmTime := 60
	bossFightTimer := 30 + chronos + 1
	bossFailCounter := 0

	local startZone := getCurrentZone()
	local zone := startZone
	zoneTime := {}
	local initiatedZone := 0
	local earliestAscendZone := 140
	local estimatedAscendLevel := gildedRanger ? abs(gildedRanger) * 250 - 75 : earliestAscendZone
	local initZone := 146
	local earlyGameZone := 175

	local tr1 := -1 ; Samurai
	local tr2 := ""

	if (gildedCheck(tr1, tr2, earlyGameZone)) {
		showSplash("Recommended transitional hero(es): " . rangers[tr1] . " > " . rangers[tr2])
	}

	local earlyGameMode := true
	gameMode := "INIT"

	showSplash("Starting Vision Run")

	showDebugSplash("Early game mode ends @ Lvl " . earlyGameZone)
	if (estimatedAscendLevel > earliestAscendZone) {
		showDebugSplash("Estimated ascension @ Lvl " . estimatedAscendLevel + 400 . " (idle), " . estimatedAscendLevel + 800 . " (active)")
	}

	startMonitoring()
	startTimer()
	reFocus()

	SetTimer, zoneTimer, 500

	loop
	{
		if (exitThread) {
			zoneTime := ""
			SetTimer, nextZoneTimer, off
			SetTimer, zoneTimer, off
			SetTimer, comboTimer, off
			clickerStop()
			stopTimer()
			stopMonitoring()
			showUserSplash("Vision Run aborted!")
			exit
		}

		; Gief moar rubies plox!
		if (mod(t, clickableHuntDelay) = 0) {
			getClickable(isClickerRunning)
		}

		; Active zone?
		if (isActiveZone(zone) or triggerActive) {
			if (!isClickerRunning) {
				; Yup, start hammering!
				clickerStart() ; ~38 CPS
				isClickerRunning := true
			}
			if (isInitiated and isClickerRunning and !isComboActive) {
				; Save combos till we have all skills
				Gosub, comboTimer
				SetTimer, comboTimer, % comboDelay * 1000 + 250
				isComboActive := true
			}
		} else if (isClickerRunning) {
			clickerStop()
			isClickerRunning := false
		}

		; Early progression
		if (zone < earlyGameZone) {
			if (manualProgression) {
				if (zone > 100) {
					showTraceSplash("Manual progression: Off")
					manualProgression := false
					SetTimer, nextZoneTimer, off
				}
			}
			if (mod(t, locateBuyUpgradesDelay) = 0 or isResuming) {
				; Scroll down when loosing track of the upgrades button
				if (!locateImage(imgBuyUpgrades)) {
					scrollToBottom()
					sleep % coinPickUpDelay * 1000
					buyAvailableUpgrades()
				}
				skillSearch := true
				isResuming := false
			}
			if (mod(t, lvlUpDelay) = 0) {
				; Level heroes bottom up
				if (locateImageUp(imgCoin, xBtn, yBtn)) {
					ctrlClick(xBtn, yBtn, 2, 1, 1)
				}
			}
			if (zone > 30 and zone > initiatedZone and mod(zone-6, 30) = 0 and zone < initZone) {
				; Lvl earlier heroes at zones 36, 66, 96 and 126
				initRun(1) ; x 100
				initiatedZone := zone
			}
		} else if (earlyGameMode) {
			showTraceSplash("Ending early game mode @ Lvl " . zone)
			earlyGameMode := false
		}

		if (!isInitiated and zone >= initZone) {
			; If enough gold, run init
			isInitiated := initRun()
			isResuming := true
		}

		; Normal progression
		if (!earlyGameMode and ((!foundTheHero and mod(t, locateGildedDelay) = 0) or isResuming)) {
			; Traverse bottom up till we find the first gilded hero/ranger we can lvl up
			if (locateGilded(xBtn, yBtn, isNew, startAt, !gildedRanger, foundTheHero)) {
				maxClick(xBtn, yBtn, 2, 1)
				if (!gildedRanger) {
					; "Power 5" mode, moving between gilded heroes
					startAt := yBtn + yWinPos - 56
				} else {
					if (isNew) {
						showDebugSplash("New gilded hero found @ Lvl " . zone)
						buyAvailableUpgrades()
					}
					skillSearch := true
				}
				isResuming := false
			} else {
				scrollToBottom()
				earlyGameZone := zone + 25
				earlyGameMode := true
				showTraceSplash("No gilded hero found yet! Early game mode extended to Lvl " . earlyGameZone)
			}
		}

		zone := getCurrentZone()

		; [Hybrid] Trigger active?
		if (!triggerActive and isHybrid() and !isClickerRunning) {
			if (zone > 10 and zone > previousIdleZone) {
				previousIdleZone := zone
				; Calculate average monster kill time for the previous 5 zones
				secPerMonster := timeBetweenZones(zone-5, zone) / 5.0 / zoneMonsters
				; Still insta-killing?
				if (secPerMonster >= maxIdleMonsterKillTime) {
					; No, trigger active
					showDebugSplash("Lvl " . zone-5 . " -> " . zone . " - Avg monster kill time: " . round(secPerMonster, 2))
					triggerActive := true
				}
			} else if (gameMode = "INIT" and isInitiated and t > 5) {
				triggerActive := true
			}
		}

		; Ascend or keep farming?
		if (zone > 10 and zone > previousZone and mod(zone-4, 5) = 0 and gameMode = "PROGRESSING") {
			; Progressing @ zone before boss
			previousZone := zone
			; Calculate average monster kill time for the previous zone
			secPerMonster := timeBetweenZones(zone-2, zone) / 2.0 / zoneMonsters
			; Fast enough to kill next boss easily?
			if (secPerMonster >= farmMonsterKillTime) {
				showDebugSplash("Lvl " . zone-2 . " -> " . zone . " - Avg monster kill time: " . round(secPerMonster, 2))
				; No, time to ascend?
				if (secPerMonster >= maxMonsterKillTime and zone > estimatedAscendLevel) {
					; Yeah, we are done here
					triggerAscension("End of run")
					break
				}
				if (gameMode = "PROGRESSING") {
					; Farm a bit before next boss
					farmTime := ceil(secPerMonster * 25)
					startFarming()
				}
			}
		}

		; Progressing?
		if (mod(t, progressCheckDelay) = 0) {
			if (!locateImage(imgProgression) and gameMode != "FARMING") {
				if (++bossFailCounter < 2) {
					setProgressionMode()
				} else {
					if (zone > estimatedAscendLevel) {
						triggerAscension("Stuck at high level boss")
						break
					} else {
						startFarming()
					}
				}
			}
		}

		; Level up...
		if (!earlyGameMode and mod(t, lvlUpDelay) = 0 and !isResuming) {
			if (matchPixelColor(blueColor, xBtn+xWinPos, yBtn+yWinPos)) {
				if (!foundTheHero) {
					; Get Bomber Max and Gog global gold and dps buffs when we can
					if (!hasBomberBuff and zone > 2930) {
						getBuff(imgMax, hasBomberBuff, skillSearch)
					} else if (!hasGogBuff and zone > 3210) {
						getBuff(imgGog, hasGogBuff, skillSearch)
					}
				}
				; ... when we can afford to do so
				ctrlClick(xBtn, yBtn, 2, 1, 1)
			} else if (gildedRanger and !matchPixelColor(goldColor, xBtn-51+xWinPos, yBtn+yWinPos)) {
				if (!matchPixelColor(brightGoldColor, xBtn-51+xWinPos, yBtn+yWinPos)) {
					; ... or not, lost sight of our gilded hero
					showTraceSplash("Trigger gilded hero locator")
					clickAwayImage(imgCombatTab)
					isResuming := true
				}
			}
		}

		if (skillSearch) {
			; Aquire possible new skills
			while (locateImage(imgSkill, xSkill, ySkill)) {
				clickPos(xSkill, ySkill, 1, 1)
				sleep 500
			}
			if (!locateImage(imgDimmedSkill)) {
				skillSearch := false
			}
		}

		zone := getCurrentZone()
		t += 1
		sleep 1000

	} until zone > getEndZone() or readyToAscend

	if (earlyGameMode) {
		maxLevels() ; get some extra souls from levels
	}

	SetTimer, nextZoneTimer, off
	SetTimer, zoneTimer, off

	if (useZoneDataLogger) {
		logZoneData(zdlStart, zone, zdlInterval)
	}
	zoneTime := ""

	SetTimer, comboTimer, off
	clickerStop()
	stopTimer()
	stopMonitoring()

	elapsedRunTime := (A_TickCount - startRunTime) / 1000
	showSplash("Vision Run duration: " . formatSeconds(elapsedRunTime))
}

gildedCheck(byref tr1, byref tr2, byref earlyGameZone) {
	global
	if (gildedRanger > 0) {
		if (gildedRanger > 3) {
			if (gildedRanger > 14) {
				; Use Wepwawet as last transitional hero for gilded Betty or Midas
				tr2 := 14
			} else {
				tr2 := gildedRanger - 3
			}
			if (gildedRanger > 6) {
				tr1 := tr2 - 3
				earlyGameZone := tr1 * 250 - 75
			}
		}
		return 1
	}
	return 0
}

triggerAscension(msg, delay:=0) {
	global
	if (!readyToAscend) {
		if (delay > 0) {
			showDebugSplash("Trigger ascension in " . floor(delay) . " seconds (" . msg . ")")
			SetTimer, ascendTimer, % -delay * 1000
		} else {
			showDebugSplash("Ascension triggered! (" . msg . ")")
			readyToAscend := true
		}
	}
}

setGameMode(newGameMode) {
	global
	showTraceSplash(gameMode . " --> " . newGameMode . " @ Lvl " . getCurrentZone())
	gameMode := newGameMode
}

isHybrid() {
	global
	return endLvlActive > endLvlIdle and endLvlIdle > 0
}

isActiveZone(zone) {
	global
	if (zone < 10) {
		return true
	}
	if (zone < endLvlActive) {
		if (endLvlIdle < endLvlActive and zone < endLvlIdle) {
			return false
		}
		return true
	}
	return false
}

getBuff(image, byref hasBuff, byref skillSearch) {
	global
	local who := image = imgMax ? "Bomber Max" : "Gog"
	scrollToBottom()
	if (upLocator(image, image.file, xImg, yImg)) {
		showDebugSplash("Get buff from " . who . " @ Lvl " . getCurrentZone())
		ctrlClick(xImg-320, yImg+43, 2, 1, 1) ; hire
		skillSearch := true
	}
	hasBuff := true
}

; Max levels early game for some extra souls
maxLevels() {
	global

	showDebugSplash("Max levels for souls")

	scrollToTop()
	reFocus()

	local xButton, yButton
	loop 9 ; pages
	{
		ControlSend,, {vk51 down}, ahk_id %chWinId% ; {q}, {vk51} or {sc010}
		sleep 500
		if (locateImage(imgMaxLvl, xButton, yButton)) {
			loop 4
			{
				clickPos(xButton, yButton, 1, 1)
				sleep % zzz
				yButton += oLvl
			}
		}
		scrollDown(5)
	}
	ControlSend,, {vk51 up}, ahk_id %chWinId%
}

clickerStart(duration := 0) {
	sendClickerMsg(WM_CLICKER_START)
	if (duration > 0) {
		SetTimer, clickerStopTimer, % -duration * 1000
	}
}

clickerPause() {
	sendClickerMsg(WM_CLICKER_PAUSE)
}

clickerStop() {
	sendClickerMsg(WM_CLICKER_STOP)
	sleep 1000
}

clickerReload() {
	sendClickerMsg(WM_CLICKER_RELOAD)
}

clickerInitialize() {
	sendClickerMsg(WM_CLICKER_INITIALIZE)
}

clickerModeFast() {
	sendClickerMsg(WM_CLICKER_MODE_FAST)
}

clickerModeSlow() {
	sendClickerMsg(WM_CLICKER_MODE_SLOW)
}

getClickerStatus() {
	return sendClickerMsg(WM_CLICKER_STATUS, 1)
}

sendClickerMsg(msg, wait:=0) {
	global
	local reply := 0
	local tmm := A_TitleMatchMode
	local dhw := A_DetectHiddenWindows
	SetTitleMatchMode, 2
	DetectHiddenWindows, on
	if (!wait) {
		PostMessage, %msg%,,,,% clickerName
	} else {
		SendMessage, %msg%,,,,% clickerName
		if (ErrorLevel != "FAIL") {
			reply := ErrorLevel
		} else {
			showWarningSplash("SendMessage failed! " . clickerScript . " started?")
		}
	}
	DetectHiddenWindows,% dhw
	SetTitleMatchMode,% tmm
	return reply
}

openSaveDialog() {
	global

	clickPos(xSettings, ySettings)
	sleep % zzz * 3
	clickPos(xSave, ySave)
	sleep % zzz * 4
}

save() {
	global
	local fileName := "ch" . A_NowUTC . ".txt"
	local newFileName := ""

	showDebugSplash("Save to " . fileName)

	; Close possible other dialog box
	ControlSend,, {esc}, ahk_class %dialogBoxClass%

	clickerPause()
	openSaveDialog()

	; Change the file name...
	if (saveMode = 1) {
		ControlSetText, Edit1, %fileName%, ahk_class %dialogBoxClass%
	} else {
		ControlSend, Edit1, %fileName%, ahk_class %dialogBoxClass%
	}
	sleep % zzz * 4
	; ... and double-check that it's correct
	ControlGetText, newFileName, Edit1, ahk_class %dialogBoxClass%
	if (newFileName = fileName) {
		ControlClick, %saveButtonClassNN%, ahk_class %dialogBoxClass%,,,, NA
	} else {
		ControlSend,, {esc}, ahk_class %dialogBoxClass%
	}

	sleep % zzz * 3
	clickPos(xSettingsClose, ySettingsClose)
	sleep % zzz
}

ascend(autoYes:=false) {
	global
	exitThread := false

	local x, y

	startMonitoring()

	if (autoYes) {
		if (autoAscendDelay > 0) {
			showWarningSplash(autoAscendDelay . " seconds till ASCENSION! (Abort with Alt+Pause)", autoAscendDelay)
			if (exitThread) {
				exitThread := false
				stopMonitoring()
				showUserSplash("Ascension aborted!")
				exit
			}
		}
	} else {
		playWarningSound()
		msgbox, 260,% script,Salvage Junk Pile & Ascend? ; default no
		ifmsgbox no
		{
			stopMonitoring()
			showUserSplash("Salvage aborted!")
			exit
		}
	}

	salvageJunkPile() ; must salvage junk relics before ascending
	setFarmMode()

	showDebugSplash("Ascend @ Lvl " . getCurrentZone())

	clickPos(xAscend, yAscend) ; 0.23 ascend button
	sleep % zzz * 4
	clickPos(xYes, yYes)
	sleep % zzz * 2

	stopMonitoring()
	sleep 1000 ; wait a sec
}

salvageJunkPile() {
	global

	switchToRelicTab()

	if (autoAscend) {
		if (displayRelicsDuration > 0) {
			clickPos(xRelic, yRelic) ; focus (will show the upgrade screen in 0.23)
		}

		if (displayRelicsDuration > 0) {
			showWarningSplash("Salvaging junk in " . displayRelicsDuration . " seconds! (Abort with Alt+Pause)", displayRelicsDuration)
			if (exitThread) {
				exitThread := false
				showUserSplash("Salvage aborted!")
				exit
			}
		}

		if (displayRelicsDuration > 0) {
			clickPos(xUpgradeNo, yUpgradeNo) ; don't upgrade
			sleep % zzz
			clickPos(xRelic+100, yRelic) ; remove focus
		}
	}

	showDebugSplash("Salvage Junk Pile")

	clickPos(xSalvageJunk, ySalvageJunk)
	sleep % zzz * 4
	clickPos(xDestroyYes, yDestroyYes)
	sleep % zzz * 2
}

buyAvailableUpgrades() {
	global
	clickPos(xBuy, yBuy, 2)
	sleep % zzz * 3
}

raid(doSpend:=0, attempts:=1) {
	global
	exitThread := false

	local xBtn := 0, yBtn := 0, raidDuration := 34 * 1000
	local wasClickerRunning := getClickerStatus()

	local mode := doSpend ? "Paid" : "Free"
	showUserSplash(mode . " Raid x " . attempts)

	Thread, NoTimers ; block timers if mid run

	switchToClanTab()
	sleep 1000
	clickAwayImage(imgClanRaid)

	if (!wasClickerRunning) {
		clickerStart()
		sleep 2500
	}
	loop % attempts
	{
		if (exitThread) {
			exitThread := false
			showUserSplash("Raid aborted!")
			break
		}
		if (clickAwayImage(imgClanCollect)) {
			break
		}
		if (doSpend) {
			showSplash(mode . " Raid (" . A_Index . "/" . attempts . ")")
			if (locateImage(imgClanFightAgain, xBtn, yBtn)) {
				while (!locateImage(imgYes)) {
					clickPos(xBtn, yBtn, 1, 1)
					sleep 250
				}
				clickAwayImage(imgYes)
				clickAwayImage(imgClanFight)
				sleep % raidDuration
			}
		} else {
			if (clickAwayImage(imgClanFight)) {
				sleep % raidDuration
			}
		}
		getClickable()
	}
	if (!wasClickerRunning) {
		clickerStop()
	}

	clickAwayImage(imgCombatTab)
	isResuming := true
}

; Toggle between farm and progression modes
toggleMode(toggle:=1) {
	global
	if (toggle) {
		; ControlSend,, {vk41}, ahk_id %chWinId% ; {a}, {vk41} or {sc01E}
		clickPos(xMode, yMode)
		sleep % zzz
	}
}

setFarmMode() {
	global
	if (!manualProgression and locateImage(imgProgression)) {
		clickAwayImage(imgProgression)
		showTraceSplash("Set Farm Mode")
	}
}

setProgressionMode() {
	global
	if (!manualProgression and locateImage(imgFarm)) {
		clickAwayImage(imgFarm)
		showTraceSplash("Set Progression Mode")
	}
}

activateSkills(skills) {
	global
	reFocus()
	loop,parse,skills,-
	{
		ControlSend,,% A_LoopField, ahk_id %chWinId%
		sleep 25
	}
}

startMonitoring() {
	global
	setTimer, checkMousePosition, 250
	setTimer, checkWindowVisibility, 20000
	if (noSleep) {
		DllCall( "SetThreadExecutionState", UInt, 0x80000003) ; ES_CONTINUOUS + ES_DISPLAY_REQUIRED + ES_SYSTEM_REQUIRED
	}
}

stopMonitoring() {
	global
	setTimer, checkMousePosition, off
	setTimer, checkWindowVisibility, off
	if (noSleep) {
		DllCall( "SetThreadExecutionState", UInt, 0x80000000) ; ES_CONTINUOUS
	}
}

handleScheduledReload(function := "") {
	global
	local params := function != "" ? "/autorun " . function : ""
	if (scheduleReload) {
		showUserSplash("Script Reload " . params, 3)
		Run "%A_AhkPath%" /restart "%A_ScriptFullPath%" %params%
	}
}

handleScheduledStop() {
	global
	if (scheduleStop) {
		showUserSplash("Scheduled stop. Exiting...")
		scheduleStop := false
		exit
	}
}

handleAutorun() {
	global
	option = %1%
	function = %2%
	if (option = "/autorun") {
		loopVisionRun()
	}
}

hasClickable() {
	global
	return locateImage(imgClickable)
}

; Try to find the first gilded hero/ranger we can lvl up
locateGilded(byref xPos, byref yPos, byref isNew, startAt:=0, silent:=0, byref foundTheHero:=0) {
	global
	isNew := 0
	local xAbs, yAbs
	local retries := 1

	if (startAt = 0 and !locateImage(imgBuyUpgrades)) {
		if (locateImage(imgCombatTab)) {
			clickAwayImage(imgCombatTab)
		}
		scrollToBottom()
	}

	if (gildedRanger = -1 and locateImage(imgBuyUpgrades)) {
		scrollUp(30) ; Locate Samurai
		foundTheHero := true
	} else if (gildedRanger = 15 and locateImage(imgBuyUpgrades) and locateImage(imgChefBuff)) {
		scrollUp(35) ; Locate Betty
		foundTheHero := true
	} else if (gildedRanger = 16 and locateImage(imgBuyUpgrades) and locateImage(imgKingsBuff)) {
		scrollUp(22) ; Locate Midas
		foundTheHero := true
	}

	while (upLocator(imgGilded, "Gilded hero", xAbs, yAbs, retries, 5, 1, startAt, silent)) {
		local xPixel := xAbs + 83 ; HI[R]E
		local yPixel := yAbs + 38
		if (matchPixelColor(dimmedYellowColor, xPixel, yPixel)) {
			startAt := yAbs
			continue
		} else if (matchPixelColor(yellowColor, xPixel, yPixel)) {
			isNew := 1
		}
		; Transform to screen relative and offset coordinates to the lvl up button
		xPos := xAbs - xWinPos + 57
		yPos := yAbs - yWinPos + 56
		return 1
	}
	return 0
}

solomonLeveler(levels) {
	global
	local x, y

	showSplash("Level Solomon x " . levels)

	switchToAncientTab()
	if (locator(imgSolomon, "Solomon", x, y)) {
		; Offset coordinates to the lvl up button
		x -= 365
		y += 28
		clickPos(x, y, levels, 1)
		sleep % zzz * 2
	}
}

checkSafetyZones() {
	global
	local window, x, y
	local i, sz
	local xL, yT, xR, yB

	MouseGetPos,,, window
	if (window = WinExist(winName)) {

		WinActivate
		MouseGetPos, x, y

		for i, sz in safetyZones
		{
			xL := getAdjustedX(sz.x1)
			yT := getAdjustedY(sz.y1)
			xR := getAdjustedX(sz.x2)
			yB := getAdjustedY(sz.y2)

			if (x > xL && y > yT && x < xR && y < yB) {
				if (sz.zone = "gilded" && !locateImage(imgGildedButton)) {
					return
				}
				Thread, NoTimers ; block timers
				playNotificationSound()
				clickerModeSlow()
				if (locateImage(imgSmile)) {
					msgbox,,% script,Click safety pause engaged. Resume?
					clickAwayImage(imgCombatTab)
					isResuming := true
					reFocus()
				} else {
					msgbox,,% script,Click safety pause engaged. Continue?
				}
				clickerModeFast()
			}
		}
	}
}

zoneMovedWithin(zone, sec) {
	loop % sec * 10 {
		sleep 100
		zoneMoved := getCurrentZone() - zone
		if (zoneMoved) {
			return zoneMoved
		}
	}
	return 0
}

nextZone() {
	global
	if (gameMode != "FARMING") {
		clickPos(xPlusOneZone, yZone)
		sleep % zzz
	}
}

timeBetweenZones(z1, z2) {
	global
	local currentTime := getTime()
	local z1time := zoneTime.HasKey(z1) ? zoneTime[z1] : currentTime
	local z2time := zoneTime.HasKey(z2) ? zoneTime[z2] : currentTime
	return abs(z1time - z2time)
}

storeZoneTime() {
	global
	local cz := getCurrentZone()
	if (cz > 0) {
		local pz := cz - 1
		local currentTime := getTime()

		if (!zoneTime.HasKey(pz)) {
			zoneTime[pz] := currentTime
		}
		if (!zoneTime.HasKey(cz)) {
			zoneTime[cz] := currentTime
			; Reached a new non-boss zone?
			if (gameMode != "PROGRESSING" and mod(cz, 5) > 0 and pz > zoneTime.MinIndex()) {
				; Yup!
				setGameMode("PROGRESSING")
			}
			bossFailCounter := 0
		}
	}
}

logZoneData(zStart, zEnd, zInterval) {
	global
	local startZone := zStart < zoneTime.MinIndex() ? zoneTime.MinIndex() : zStart
	local endZone := zEnd > zoneTime.MaxIndex() ? zoneTime.MaxIndex() : zEnd
	local intervals := ceil((endZone - startZone) / zInterval)

	if (startZone >= endZone) {
		return 0
	}

	local zone := startZone
	local prevZone := zone - zInterval

	local totalTime := 0
	local intervalTime := 0

	local t := "`t" ; tab
	local nl := "`n" ; new line
	local zoneData := "Zones: " . startZone . " -> " . endZone . ", Interval: " . zInterval
	zoneData .= nl . "Zone" . t . "Time" . t . "Diff (s)"
	zoneData .= nl . zone . t . "00:00:00" . t . "0"

	loop % intervals {
		zone += zInterval
		if (zone > endZone) {
			zone := endZone
		}
		prevZone += zInterval

		totalTime := timeBetweenZones(startZone, zone)
		intervalTime := timeBetweenZones(prevZone, zone)

		zoneData .= nl . zone . t . formatSeconds(totalTime) . t . round(intervalTime, 1)
	}

	logger(zoneData, "INFO", "_zone_data")
}

startFarming() {
	global
	if (gameMode != "FARMING") {
		setGameMode("FARMING")
		setFarmMode()
		scrollToZone(zoneTime.MaxIndex())
		local farmZone := getCurrentZone()
		if (mod(farmZone, 5) = 0) {
			scrollToZone(--farmZone)
		}
		; Farm on highest recorded non-boss zone
		showDebugSplash("Farm @ Lvl " . farmZone . " for " . farmTime . "s")
		SetTimer, farmTimer, % -farmTime * 1000
	}
}

bossFight() {
	global
	if (gameMode != "FIGHTING") {
		setGameMode("FIGHTING")
		setProgressionMode()
		SetTimer, bossTimer, % -bossFightTimer * 1000, 2 ; prio 2
	}
}

startTimer() {
	global
	SetTimer, timeTick, 250, 4 ; prio 4
}

stopTimer() {
	global
	SetTimer, timeTick, off
}

getTime() {
	global
	return elapsedTime
}

getCurrentZone() {
	global
	local zone
	; Try get the current zone lvl
	loop 2 {
		zone := getZone()
		if (zone > 0) {
			return zone
		}
		sleep % zzz
	}
	showTraceSplash("Failed to get current zone lvl!")
	return zoneTime.MaxIndex()
}

; -----------------------------------------------------------------------------------------
; -- Subroutines
; -----------------------------------------------------------------------------------------

timeTick:
	elapsedTime += 0.25
return

; Safety zone around the in-game tabs (that triggers an automatic script pause when breached)
checkMousePosition:
	checkSafetyZones()
return

checkWindowVisibility:
	if (!locateImage(imgSmile)) {
		WinActivate, ahk_id %chWinId%
	}
return

comboTimer:
	activateSkills(skillCombo[comboIndex])
	comboIndex := comboIndex < skillCombo.MaxIndex() ? comboIndex+1 : 2
return

clickerStopTimer:
	clickerStop()
return

farmTimer:
	if (gameMode != "PROGRESSING") {
		bossFight()
	}
return

bossTimer:
	if (!locateImage(imgProgression)) {
		; Failed the boss, back to farming...
		showDebugSplash("Failed boss!")
		startFarming()
	}
return

ascendTimer:
	readyToAscend := true
return

zoneTimer:
	storeZoneTime()
return

nextZoneTimer:
	nextZone()
return

; -----------------------------------------------------------------------------------------
; Clicker Heroes Sw1ft Bot
; by Sw1ftb
; -----------------------------------------------------------------------------------------

; #Warn All
#Persistent
#NoEnv
#InstallKeybdHook

#Include %A_ScriptDir%
#Include ch_bot_lib.ahk

SetControlDelay, -1

scriptName=CH Sw1ft Bot
scriptVersion=4.2.0
minLibVersion=4.0

script := scriptName . " v" . scriptVersion

clickerScript := "monster_clicker.ahk"
clickerName := clickerScript . " - AutoHotkey"

scheduleReload := false
scheduleStop := false
manualProgression := false

elapsedTime := 0.0

; -----------------------------------------------------------------------------------------

#Include system\wm_messages.ahk

; Load system default settings
#Include system\ch_bot_default_settings.ahk

IfNotExist, ch_bot_settings.ahk
{
	FileCopy, system\ch_bot_default_settings.ahk, ch_bot_settings.ahk
}

; Load user settings
#Include *i ch_bot_settings.ahk

if (libVersion != minLibVersion) {
	showWarningSplash("The bot lib version must be " . minLibVersion . "!")
	ExitApp
}

SysGet, VirtualScreenWidth, 78
SysGet, VirtualScreenHeight, 79

logVariable("A_AhkVersion", A_AhkVersion)
logVariable("A_OSVersion", A_OSVersion)
logVariable("A_ScreenWidth", A_ScreenWidth)
logVariable("A_ScreenHeight", A_ScreenHeight)
if (VirtualScreenWidth > A_ScreenHeight) {
	logVariable("VirtualScreenWidth", VirtualScreenWidth)
	logVariable("VirtualScreenHeight", VirtualScreenHeight)
}
logVariable("A_ScreenDPI", A_ScreenDPI )
logVariable("script", script)

clientCheck()

Run, "%A_ScriptDir%\%clickerScript%",, UseErrorLevel
if (ErrorLevel != 0) {
	playWarningSound()
	msgbox,,% script,% "Failed to auto-start " . clickerScript . " (system error code = " . A_LastError . ")!"
}

handleAutorun()

; -----------------------------------------------------------------------------------------
; -- Hotkeys (+=Shift, !=Alt, ^=Ctrl, #=Win)
; -----------------------------------------------------------------------------------------

; Show the cursor position with Alt+Middle Mouse Button
!mbutton::
	mousegetpos, xpos, ypos
	msgbox,,% script,% "Cursor position: x" xpos-leftMarginOffset " y" ypos-topMarginOffset
return

; Top margin calculation when clicking in the center of the ancient tab eye
^mbutton::
	mousegetpos, xpos, ypos
	msgbox,,% script,% "Set browserTopMargin to: " . ypos-102
return

; -- Main Hotkeys -------------------------------------------------------------------------

; Start a Vision Run loop with Ctrl+F1
^F1::
	loopVisionRun()
return

; Reload and restart Vision Run loop with Ctrl+F3
^F3::
	global scheduleReload := true
	handleScheduledReload("loopVisionRun")
return

; Pause/unpause the script
~Pause::Pause
return

; Abort any active run or initiated ascension with Alt+Pause
!Pause::
	showUserSplash("Aborting...")
	exitThread := true
return
Hotkey, !Pause, , P10

; Schedule a stop after finishing the current run with Shift+Pause
+Pause::
	toggleFlag("scheduleStop", scheduleStop)
return

; Reload the script (needed after configuration changes)
!F5::
	global scheduleReload := true
	handleScheduledReload()
return
Hotkey, !F5, , P10

; Schedule a script reload after finishing the current run, then restart it
+^F5::
	toggleFlag("scheduleReload", scheduleReload)
return

; Re-initialize coordinates (needed after moving or re-sizing the client window)
!F6::
	showUserSplash("Re-initialize coordinates... ")
	clientCheck()
	clickerInitialize()
return
Hotkey, !F6, , P10

; -- Supplementary Hotkeys ----------------------------------------------------------------

; Suspend/Unsuspend all other Hotkeys with Ctrl+Esc
~^Esc::Suspend, Toggle
return

; Autosave the game
^F11::
	save()
return
Hotkey, ^F11, , P5

; Raid once for free with Win+F6
#F6::
	raid()
return
Hotkey, #F6, , P5

; One paid raid
#F7::
	raid(1)
return
Hotkey, #F7, , P5

; Paid raids
#F8::
	raid(1, raidAttempts)
return
Hotkey, #F8, , P5

; Toggle boolean (true/false) flags with Shift+Ctrl+Fx

+^F1::
	toggleFlag("autoAscend", autoAscend)
return

+^F6::
	toggleFlag("playNotificationSounds", playNotificationSounds)
return
Hotkey, +^F6, , P5

+^F7::
	toggleFlag("playWarningSounds", playWarningSounds)
return

+^F11::
	toggleFlag("saveBeforeAscending", saveBeforeAscending)
return

; -- Test Hotkeys -------------------------------------------------------------------------

; Ctrl+Alt+F1 should scroll down to the bottom, then back up
^!F1::
	scrollToBottom()
	scrollToTop()
return

; Ctrl+Alt+F2 should switch between all used tabs
^!F2::
	switchToAncientTab()
	switchToRelicTab()
	switchToClanTab()
	switchToCombatTab()
return

; -----------------------------------------------------------------------------------------
; -- Functions
; -----------------------------------------------------------------------------------------

getClickable(active:=1) {
	global
	local xPos, yPos
	if (active = 1) {
		clickPos(524, 487)
		clickPos(747, 431)
		clickPos(760, 380)
		clickPos(873, 512)
		clickPos(1005, 453)
		clickPos(1053, 443)
	} else {
		loop % clickableImageFiles.Length()
		{
			imgClickable.file := clickableImageFiles[A_Index]
			if (locateImage(imgClickable, xPos, yPos)) {
				clickPos(xPos, yPos, 1, 1) ; absolute pos
				break
			}
		}
	}
}

; Level up and upgrade all heroes
initRun(initMode:=0) {
	global
	local hasAscendButton := false
	local hasSkillBar := false
	local hasNoSkillLocked := false

	showDebugSplash("Init Run @ Lvl " . getCurrentZone())

	switchToCombatTab()
	scrollToTop()
	reFocus()

	local foundDK := false
	local xButton, yButton, xDK, yDK, x
	local clickCount := initMode = 1 ? 1 : 2

	locateImage(imgCoin, xButton, yButton) ; get a x coordinate for the hire/lvl up buttons

	loop 10 ; pages
	{
		if (locateImage(imgDK, xDK, yDK) or locateImage(imgDKG, xDK, yDK)) {
			foundDK := true
		}
		loop 5 ; attempts per page
		{
			if (locateImage(imgDimmedSkill, x, yButton)
					or locateImage(imgHire, x, yButton)) {
				if (foundDK and yButton > yDK) {
					; Don't level anything below Frostleaf
					continue
				}
				ctrlClick(xButton, yButton, clickCount, 1, 1)
			} else {
				break
			}
		}
		if (foundDK) {
			if (locateImage(imgAscend)) {
				hasAscendButton := true
			}
			break
		}
		scrollDown(5)
	}
	scrollToBottom()
	buyAvailableUpgrades()

	if (initMode = 0) {
		if (locateImage(imgSkillBar)) {
			hasSkillBar := true
		}
		if (!locateImage(imgSkillLocked)) {
			hasNoSkillLocked := true
		}
		logVariable("hasAscendButton", hasAscendButton, true, "TRACE")
		logVariable("hasSkillBar", hasSkillBar, true, "TRACE")
		logVariable("hasNoSkillLocked", hasNoSkillLocked, true, "TRACE")
	}

	return hasAscendButton and hasSkillBar and hasNoSkillLocked
}

getEndZone() {
	global
	return endLvlActive > endLvlIdle ? endLvlActive : endLvlIdle
}

getState() {
	global

	if (!WinExist(winName)) {
		return -2 ; no ch window
	}
	if (getCurrentZone() = 0) {
		return -1 ; vision, but not in browser
	}
	if (!locateImage(imgSmile)) {
		return 0 ; vision, but not finding anything
	}
	if (getCurrentZone() < getEndZone() and !readyToAscend) {
		return 1 ; ready for progression
	} else {
		return 2 ; ready to ascend
	}
}

loopVisionRun() {
	global
	readyToAscend := false

	local state := 0

	showUserSplash("Starting Vision Runs!")

	logVariable("browser", browser)
	logVariable("browserTopMargin", browserTopMargin)
	logVariable("gildedRanger", rangers[gildedRanger])
	logVariable("endLvlIdle", endLvlIdle)
	logVariable("endLvlActive", endLvlActive)
	logVariable("maxIdleMonsterKillTime", maxIdleMonsterKillTime)
	logVariable("maxMonsterKillTime", maxMonsterKillTime)
	logVariable("chronos", chronos)
	logVariable("kumawakamaru", kumawakamaru)
	logVariable("vaagur", vaagur)
	if (endLvlActive > 0) {
		logArray("skillCombo", skillCombo)
	}
	logVariable("clickableHuntDelay", clickableHuntDelay)
	logVariable("saveBeforeAscending", saveBeforeAscending, true)
	logVariable("autoAscend", autoAscend, true)
	logVariable("noSleep", noSleep, true)
	logVariable("levelSolomon", levelSolomon, true)

	loop
	{
		state := getState()
		if (state < 1) {
			showWarningSplash("Start failed (state = " . state . ")! Trying again...")
			clientCheck()
			clickerInitialize()
			continue
		}
		if (getState() = 1) {
			visionRun()
		}
		if (getState() = 2) {
			if (saveBeforeAscending) {
				save()
			}
			ascend(autoAscend)
			readyToAscend := false
			if (levelSolomon) {
				solomonLeveler(solomonLevels)
			}
			handleScheduledStop()
			handleScheduledReload("loopVisionRun")
		}
	}
}

visionRun() {
	global
	exitThread := false
	isResuming := false

	local startRunTime := A_TickCount

	local isInitiated := false
	local isClickerRunning := false
	local isComboActive := false
	local triggerActive := false
	local hasBomberBuff := gildedRanger = 12 ? true : false
	local hasGogBuff := gildedRanger = 13 ? true : false
	local foundTheHero := false

	local xBtn := 0, yBtn := 0, isNew := 0, startAt := 0
	local xSkill := 0, ySkill := 0, skillSearch := false

	local locateGildedDelay := gildedRanger ? 60 : 4
	local locateBuyUpgradesDelay := 20
	local progressCheckDelay := 10

	local t := 0
	local elapsedRunTime := 0

	local comboDelay := skillCombo[1]
	comboIndex := 2

	manualProgression := false
	switchToCombatTab()
	setProgressionMode()
	if (!locateImage(imgProgression)) {
		showTraceSplash("Manual progression: On")
		manualProgression := true
		SetTimer, nextZoneTimer, 500
	}

	local previousZone := 0
	local previousIdleZone := 0

	local zoneMonsters := 10.0 + kumawakamaru
	local secPerMonster := 0
	local farmMonsterKillTime := 2.0 ; stop and farm before boss when exceeded

	farmTime := 60
	bossFightTimer := 30 + chronos + 1
	bossFailCounter := 0

	local startZone := getCurrentZone()
	local zone := startZone
	zoneTime := {}
	local initiatedZone := 0
	local earliestAscendZone := 140
	local estimatedAscendLevel := gildedRanger ? abs(gildedRanger) * 250 - 75 : earliestAscendZone
	local initZone := 146
	local earlyGameZone := 175

	local tr1 := -1 ; Samurai
	local tr2 := ""

	if (gildedCheck(tr1, tr2, earlyGameZone)) {
		showSplash("Recommended transitional hero(es): " . rangers[tr1] . " > " . rangers[tr2])
	}

	local earlyGameMode := true
	gameMode := "INIT"

	showSplash("Starting Vision Run")

	showDebugSplash("Early game mode ends @ Lvl " . earlyGameZone)
	if (estimatedAscendLevel > earliestAscendZone) {
		showDebugSplash("Estimated ascension @ Lvl " . estimatedAscendLevel + 400 . " (idle), " . estimatedAscendLevel + 800 . " (active)")
	}

	startMonitoring()
	startTimer()
	reFocus()

	SetTimer, zoneTimer, 500

	loop
	{
		if (exitThread) {
			zoneTime := ""
			SetTimer, nextZoneTimer, off
			SetTimer, zoneTimer, off
			SetTimer, comboTimer, off
			clickerStop()
			stopTimer()
			stopMonitoring()
			showUserSplash("Vision Run aborted!")
			exit
		}

		; Gief moar rubies plox!
		if (mod(t, clickableHuntDelay) = 0) {
			getClickable(isClickerRunning)
		}

		; Active zone?
		if (isActiveZone(zone) or triggerActive) {
			if (!isClickerRunning) {
				; Yup, start hammering!
				clickerStart() ; ~38 CPS
				isClickerRunning := true
			}
			if (isInitiated and isClickerRunning and !isComboActive) {
				; Save combos till we have all skills
				Gosub, comboTimer
				SetTimer, comboTimer, % comboDelay * 1000 + 250
				isComboActive := true
			}
		} else if (isClickerRunning) {
			clickerStop()
			isClickerRunning := false
		}

		; Early progression
		if (zone < earlyGameZone) {
			if (manualProgression) {
				if (zone > 100) {
					showTraceSplash("Manual progression: Off")
					manualProgression := false
					SetTimer, nextZoneTimer, off
				}
			}
			if (mod(t, locateBuyUpgradesDelay) = 0 or isResuming) {
				; Scroll down when loosing track of the upgrades button
				if (!locateImage(imgBuyUpgrades)) {
					scrollToBottom()
					sleep % coinPickUpDelay * 1000
					buyAvailableUpgrades()
				}
				skillSearch := true
				isResuming := false
			}
			if (mod(t, lvlUpDelay) = 0) {
				; Level heroes bottom up
				if (locateImageUp(imgCoin, xBtn, yBtn)) {
					ctrlClick(xBtn, yBtn, 2, 1, 1)
				}
			}
			if (zone > 30 and zone > initiatedZone and mod(zone-6, 30) = 0 and zone < initZone) {
				; Lvl earlier heroes at zones 36, 66, 96 and 126
				initRun(1) ; x 100
				initiatedZone := zone
			}
		} else if (earlyGameMode) {
			showTraceSplash("Ending early game mode @ Lvl " . zone)
			earlyGameMode := false
		}

		if (!isInitiated and zone >= initZone) {
			; If enough gold, run init
			isInitiated := initRun()
			isResuming := true
		}

		; Normal progression
		if (!earlyGameMode and ((!foundTheHero and mod(t, locateGildedDelay) = 0) or isResuming)) {
			; Traverse bottom up till we find the first gilded hero/ranger we can lvl up
			if (locateGilded(xBtn, yBtn, isNew, startAt, !gildedRanger, foundTheHero)) {
				maxClick(xBtn, yBtn, 2, 1)
				if (!gildedRanger) {
					; "Power 5" mode, moving between gilded heroes
					startAt := yBtn + yWinPos - 56
				} else {
					if (isNew) {
						showDebugSplash("New gilded hero found @ Lvl " . zone)
						buyAvailableUpgrades()
					}
					skillSearch := true
				}
				isResuming := false
			} else {
				scrollToBottom()
				earlyGameZone := zone + 25
				earlyGameMode := true
				showTraceSplash("No gilded hero found yet! Early game mode extended to Lvl " . earlyGameZone)
			}
		}

		zone := getCurrentZone()

		; [Hybrid] Trigger active?
		if (!triggerActive and isHybrid() and !isClickerRunning) {
			if (zone > 10 and zone > previousIdleZone) {
				previousIdleZone := zone
				; Calculate average monster kill time for the previous 5 zones
				secPerMonster := timeBetweenZones(zone-5, zone) / 5.0 / zoneMonsters
				; Still insta-killing?
				if (secPerMonster >= maxIdleMonsterKillTime) {
					; No, trigger active
					showDebugSplash("Lvl " . zone-5 . " -> " . zone . " - Avg monster kill time: " . round(secPerMonster, 2))
					triggerActive := true
				}
			} else if (gameMode = "INIT" and isInitiated and t > 5) {
				triggerActive := true
			}
		}

		; Ascend or keep farming?
		if (zone > 10 and zone > previousZone and mod(zone-4, 5) = 0 and gameMode = "PROGRESSING") {
			; Progressing @ zone before boss
			previousZone := zone
			; Calculate average monster kill time for the previous zone
			secPerMonster := timeBetweenZones(zone-2, zone) / 2.0 / zoneMonsters
			; Fast enough to kill next boss easily?
			if (secPerMonster >= farmMonsterKillTime) {
				showDebugSplash("Lvl " . zone-2 . " -> " . zone . " - Avg monster kill time: " . round(secPerMonster, 2))
				; No, time to ascend?
				if (secPerMonster >= maxMonsterKillTime and zone > estimatedAscendLevel) {
					; Yeah, we are done here
					triggerAscension("End of run")
					break
				}
				if (gameMode = "PROGRESSING") {
					; Farm a bit before next boss
					farmTime := ceil(secPerMonster * 25)
					startFarming()
				}
			}
		}

		; Progressing?
		if (mod(t, progressCheckDelay) = 0) {
			if (!locateImage(imgProgression) and gameMode != "FARMING") {
				if (++bossFailCounter < 2) {
					setProgressionMode()
				} else {
					if (zone > estimatedAscendLevel) {
						triggerAscension("Stuck at high level boss")
						break
					} else {
						startFarming()
					}
				}
			}
		}

		; Level up...
		if (!earlyGameMode and mod(t, lvlUpDelay) = 0 and !isResuming) {
			if (matchPixelColor(blueColor, xBtn+xWinPos, yBtn+yWinPos)) {
				if (!foundTheHero) {
					; Get Bomber Max and Gog global gold and dps buffs when we can
					if (!hasBomberBuff and zone > 2930) {
						getBuff(imgMax, hasBomberBuff, skillSearch)
					} else if (!hasGogBuff and zone > 3210) {
						getBuff(imgGog, hasGogBuff, skillSearch)
					}
				}
				; ... when we can afford to do so
				ctrlClick(xBtn, yBtn, 2, 1, 1)
			} else if (gildedRanger and !matchPixelColor(goldColor, xBtn-51+xWinPos, yBtn+yWinPos)) {
				if (!matchPixelColor(brightGoldColor, xBtn-51+xWinPos, yBtn+yWinPos)) {
					; ... or not, lost sight of our gilded hero
					showTraceSplash("Trigger gilded hero locator")
					clickAwayImage(imgCombatTab)
					isResuming := true
				}
			}
		}

		if (skillSearch) {
			; Aquire possible new skills
			while (locateImage(imgSkill, xSkill, ySkill)) {
				clickPos(xSkill, ySkill, 1, 1)
				sleep 500
			}
			if (!locateImage(imgDimmedSkill)) {
				skillSearch := false
			}
		}

		zone := getCurrentZone()
		t += 1
		sleep 1000

	} until zone > getEndZone() or readyToAscend

	if (earlyGameMode) {
		maxLevels() ; get some extra souls from levels
	}

	SetTimer, nextZoneTimer, off
	SetTimer, zoneTimer, off

	if (useZoneDataLogger) {
		logZoneData(zdlStart, zone, zdlInterval)
	}
	zoneTime := ""

	SetTimer, comboTimer, off
	clickerStop()
	stopTimer()
	stopMonitoring()

	elapsedRunTime := (A_TickCount - startRunTime) / 1000
	showSplash("Vision Run duration: " . formatSeconds(elapsedRunTime))
}

gildedCheck(byref tr1, byref tr2, byref earlyGameZone) {
	global
	if (gildedRanger > 0) {
		if (gildedRanger > 3) {
			if (gildedRanger > 14) {
				; Use Wepwawet as last transitional hero for gilded Betty or Midas
				tr2 := 14
			} else {
				tr2 := gildedRanger - 3
			}
			if (gildedRanger > 6) {
				tr1 := tr2 - 3
				earlyGameZone := tr1 * 250 - 75
			}
		}
		return 1
	}
	return 0
}

triggerAscension(msg, delay:=0) {
	global
	if (!readyToAscend) {
		if (delay > 0) {
			showDebugSplash("Trigger ascension in " . floor(delay) . " seconds (" . msg . ")")
			SetTimer, ascendTimer, % -delay * 1000
		} else {
			showDebugSplash("Ascension triggered! (" . msg . ")")
			readyToAscend := true
		}
	}
}

setGameMode(newGameMode) {
	global
	showTraceSplash(gameMode . " --> " . newGameMode . " @ Lvl " . getCurrentZone())
	gameMode := newGameMode
}

isHybrid() {
	global
	return endLvlActive > endLvlIdle and endLvlIdle > 0
}

isActiveZone(zone) {
	global
	if (zone < 10) {
		return true
	}
	if (zone < endLvlActive) {
		if (endLvlIdle < endLvlActive and zone < endLvlIdle) {
			return false
		}
		return true
	}
	return false
}

getBuff(image, byref hasBuff, byref skillSearch) {
	global
	local who := image = imgMax ? "Bomber Max" : "Gog"
	scrollToBottom()
	if (upLocator(image, image.file, xImg, yImg)) {
		showDebugSplash("Get buff from " . who . " @ Lvl " . getCurrentZone())
		ctrlClick(xImg-320, yImg+43, 2, 1, 1) ; hire
		skillSearch := true
	}
	hasBuff := true
}

; Max levels early game for some extra souls
maxLevels() {
	global

	showDebugSplash("Max levels for souls")

	scrollToTop()
	reFocus()

	local xButton, yButton
	loop 9 ; pages
	{
		ControlSend,, {vk51 down}, ahk_id %chWinId% ; {q}, {vk51} or {sc010}
		sleep 500
		if (locateImage(imgMaxLvl, xButton, yButton)) {
			loop 4
			{
				clickPos(xButton, yButton, 1, 1)
				sleep % zzz
				yButton += oLvl
			}
		}
		scrollDown(5)
	}
	ControlSend,, {vk51 up}, ahk_id %chWinId%
}

clickerStart(duration := 0) {
	sendClickerMsg(WM_CLICKER_START)
	if (duration > 0) {
		SetTimer, clickerStopTimer, % -duration * 1000
	}
}

clickerPause() {
	sendClickerMsg(WM_CLICKER_PAUSE)
}

clickerStop() {
	sendClickerMsg(WM_CLICKER_STOP)
	sleep 1000
}

clickerReload() {
	sendClickerMsg(WM_CLICKER_RELOAD)
}

clickerInitialize() {
	sendClickerMsg(WM_CLICKER_INITIALIZE)
}

clickerModeFast() {
	sendClickerMsg(WM_CLICKER_MODE_FAST)
}

clickerModeSlow() {
	sendClickerMsg(WM_CLICKER_MODE_SLOW)
}

getClickerStatus() {
	return sendClickerMsg(WM_CLICKER_STATUS, 1)
}

sendClickerMsg(msg, wait:=0) {
	global
	local reply := 0
	local tmm := A_TitleMatchMode
	local dhw := A_DetectHiddenWindows
	SetTitleMatchMode, 2
	DetectHiddenWindows, on
	if (!wait) {
		PostMessage, %msg%,,,,% clickerName
	} else {
		SendMessage, %msg%,,,,% clickerName
		if (ErrorLevel != "FAIL") {
			reply := ErrorLevel
		} else {
			showWarningSplash("SendMessage failed! " . clickerScript . " started?")
		}
	}
	DetectHiddenWindows,% dhw
	SetTitleMatchMode,% tmm
	return reply
}

openSaveDialog() {
	global

	clickPos(xSettings, ySettings)
	sleep % zzz * 3
	clickPos(xSave, ySave)
	sleep % zzz * 4
}

save() {
	global
	local fileName := "ch" . A_NowUTC . ".txt"
	local newFileName := ""

	showDebugSplash("Save to " . fileName)

	; Close possible other dialog box
	ControlSend,, {esc}, ahk_class %dialogBoxClass%

	clickerPause()
	openSaveDialog()

	; Change the file name...
	if (saveMode = 1) {
		ControlSetText, Edit1, %fileName%, ahk_class %dialogBoxClass%
	} else {
		ControlSend, Edit1, %fileName%, ahk_class %dialogBoxClass%
	}
	sleep % zzz * 4
	; ... and double-check that it's correct
	ControlGetText, newFileName, Edit1, ahk_class %dialogBoxClass%
	if (newFileName = fileName) {
		ControlClick, %saveButtonClassNN%, ahk_class %dialogBoxClass%,,,, NA
	} else {
		ControlSend,, {esc}, ahk_class %dialogBoxClass%
	}

	sleep % zzz * 3
	clickPos(xSettingsClose, ySettingsClose)
	sleep % zzz
}

ascend(autoYes:=false) {
	global
	exitThread := false

	local x, y

	startMonitoring()

	if (autoYes) {
		if (autoAscendDelay > 0) {
			showWarningSplash(autoAscendDelay . " seconds till ASCENSION! (Abort with Alt+Pause)", autoAscendDelay)
			if (exitThread) {
				exitThread := false
				stopMonitoring()
				showUserSplash("Ascension aborted!")
				exit
			}
		}
	} else {
		playWarningSound()
		msgbox, 260,% script,Salvage Junk Pile & Ascend? ; default no
		ifmsgbox no
		{
			stopMonitoring()
			showUserSplash("Salvage aborted!")
			exit
		}
	}

	salvageJunkPile() ; must salvage junk relics before ascending
	setFarmMode()

	showDebugSplash("Ascend @ Lvl " . getCurrentZone())

	clickPos(xAscend, yAscend) ; 0.23 ascend button
	sleep % zzz * 4
	clickPos(xYes, yYes)
	sleep % zzz * 2

	stopMonitoring()
	sleep 1000 ; wait a sec
}

salvageJunkPile() {
	global

	switchToRelicTab()

	if (autoAscend) {
		if (displayRelicsDuration > 0) {
			clickPos(xRelic, yRelic) ; focus (will show the upgrade screen in 0.23)
		}

		if (displayRelicsDuration > 0) {
			showWarningSplash("Salvaging junk in " . displayRelicsDuration . " seconds! (Abort with Alt+Pause)", displayRelicsDuration)
			if (exitThread) {
				exitThread := false
				showUserSplash("Salvage aborted!")
				exit
			}
		}

		if (displayRelicsDuration > 0) {
			clickPos(xUpgradeNo, yUpgradeNo) ; don't upgrade
			sleep % zzz
			clickPos(xRelic+100, yRelic) ; remove focus
		}
	}

	showDebugSplash("Salvage Junk Pile")

	clickPos(xSalvageJunk, ySalvageJunk)
	sleep % zzz * 4
	clickPos(xDestroyYes, yDestroyYes)
	sleep % zzz * 2
}

buyAvailableUpgrades() {
	global
	clickPos(xBuy, yBuy, 2)
	sleep % zzz * 3
}

raid(doSpend:=0, attempts:=1) {
	global
	exitThread := false

	local xBtn := 0, yBtn := 0, raidDuration := 34 * 1000
	local wasClickerRunning := getClickerStatus()

	local mode := doSpend ? "Paid" : "Free"
	showUserSplash(mode . " Raid x " . attempts)

	Thread, NoTimers ; block timers if mid run

	switchToClanTab()
	sleep 1000
	clickAwayImage(imgClanRaid)

	if (!wasClickerRunning) {
		clickerStart()
		sleep 2500
	}
	loop % attempts
	{
		if (exitThread) {
			exitThread := false
			showUserSplash("Raid aborted!")
			break
		}
		if (clickAwayImage(imgClanCollect)) {
			break
		}
		if (doSpend) {
			showSplash(mode . " Raid (" . A_Index . "/" . attempts . ")")
			if (locateImage(imgClanFightAgain, xBtn, yBtn)) {
				while (!locateImage(imgYes)) {
					clickPos(xBtn, yBtn, 1, 1)
					sleep 250
				}
				clickAwayImage(imgYes)
				clickAwayImage(imgClanFight)
				sleep % raidDuration
			}
		} else {
			if (clickAwayImage(imgClanFight)) {
				sleep % raidDuration
			}
		}
		getClickable()
	}
	if (!wasClickerRunning) {
		clickerStop()
	}

	clickAwayImage(imgCombatTab)
	isResuming := true
}

; Toggle between farm and progression modes
toggleMode(toggle:=1) {
	global
	if (toggle) {
		; ControlSend,, {vk41}, ahk_id %chWinId% ; {a}, {vk41} or {sc01E}
		clickPos(xMode, yMode)
		sleep % zzz
	}
}

setFarmMode() {
	global
	if (!manualProgression and locateImage(imgProgression)) {
		clickAwayImage(imgProgression)
		showTraceSplash("Set Farm Mode")
	}
}

setProgressionMode() {
	global
	if (!manualProgression and locateImage(imgFarm)) {
		clickAwayImage(imgFarm)
		showTraceSplash("Set Progression Mode")
	}
}

activateSkills(skills) {
	global
	reFocus()
	loop,parse,skills,-
	{
		ControlSend,,% A_LoopField, ahk_id %chWinId%
		sleep 25
	}
}

startMonitoring() {
	global
	setTimer, checkMousePosition, 250
	setTimer, checkWindowVisibility, 20000
	if (noSleep) {
		DllCall( "SetThreadExecutionState", UInt, 0x80000003) ; ES_CONTINUOUS + ES_DISPLAY_REQUIRED + ES_SYSTEM_REQUIRED
	}
}

stopMonitoring() {
	global
	setTimer, checkMousePosition, off
	setTimer, checkWindowVisibility, off
	if (noSleep) {
		DllCall( "SetThreadExecutionState", UInt, 0x80000000) ; ES_CONTINUOUS
	}
}

handleScheduledReload(function := "") {
	global
	local params := function != "" ? "/autorun " . function : ""
	if (scheduleReload) {
		showUserSplash("Script Reload " . params, 3)
		Run "%A_AhkPath%" /restart "%A_ScriptFullPath%" %params%
	}
}

handleScheduledStop() {
	global
	if (scheduleStop) {
		showUserSplash("Scheduled stop. Exiting...")
		scheduleStop := false
		exit
	}
}

handleAutorun() {
	global
	option = %1%
	function = %2%
	if (option = "/autorun") {
		loopVisionRun()
	}
}

hasClickable() {
	global
	return locateImage(imgClickable)
}

; Try to find the first gilded hero/ranger we can lvl up
locateGilded(byref xPos, byref yPos, byref isNew, startAt:=0, silent:=0, byref foundTheHero:=0) {
	global
	isNew := 0
	local xAbs, yAbs
	local retries := 1

	if (startAt = 0 and !locateImage(imgBuyUpgrades)) {
		if (locateImage(imgCombatTab)) {
			clickAwayImage(imgCombatTab)
		}
		scrollToBottom()
	}

	if (gildedRanger = -1 and locateImage(imgBuyUpgrades)) {
		scrollUp(30) ; Locate Samurai
		foundTheHero := true
	} else if (gildedRanger = 15 and locateImage(imgBuyUpgrades) and locateImage(imgChefBuff)) {
		scrollUp(35) ; Locate Betty
		foundTheHero := true
	} else if (gildedRanger = 16 and locateImage(imgBuyUpgrades) and locateImage(imgKingsBuff)) {
		scrollUp(22) ; Locate Midas
		foundTheHero := true
	}

	while (upLocator(imgGilded, "Gilded hero", xAbs, yAbs, retries, 5, 1, startAt, silent)) {
		local xPixel := xAbs + 83 ; HI[R]E
		local yPixel := yAbs + 38
		if (matchPixelColor(dimmedYellowColor, xPixel, yPixel)) {
			startAt := yAbs
			continue
		} else if (matchPixelColor(yellowColor, xPixel, yPixel)) {
			isNew := 1
		}
		; Transform to screen relative and offset coordinates to the lvl up button
		xPos := xAbs - xWinPos + 57
		yPos := yAbs - yWinPos + 56
		return 1
	}
	return 0
}

solomonLeveler(levels) {
	global
	local x, y

	showSplash("Level Solomon x " . levels)

	switchToAncientTab()
	if (locator(imgSolomon, "Solomon", x, y)) {
		; Offset coordinates to the lvl up button
		x -= 365
		y += 28
		clickPos(x, y, levels, 1)
		sleep % zzz * 2
	}
}

checkSafetyZones() {
	global
	local window, x, y
	local i, sz
	local xL, yT, xR, yB

	MouseGetPos,,, window
	if (window = WinExist(winName)) {

		WinActivate
		MouseGetPos, x, y

		for i, sz in safetyZones
		{
			xL := getAdjustedX(sz.x1)
			yT := getAdjustedY(sz.y1)
			xR := getAdjustedX(sz.x2)
			yB := getAdjustedY(sz.y2)

			if (x > xL && y > yT && x < xR && y < yB) {
				if (sz.zone = "gilded" && !locateImage(imgGildedButton)) {
					return
				}
				Thread, NoTimers ; block timers
				playNotificationSound()
				clickerModeSlow()
				if (locateImage(imgSmile)) {
					msgbox,,% script,Click safety pause engaged. Resume?
					clickAwayImage(imgCombatTab)
					isResuming := true
					reFocus()
				} else {
					msgbox,,% script,Click safety pause engaged. Continue?
				}
				clickerModeFast()
			}
		}
	}
}

zoneMovedWithin(zone, sec) {
	loop % sec * 10 {
		sleep 100
		zoneMoved := getCurrentZone() - zone
		if (zoneMoved) {
			return zoneMoved
		}
	}
	return 0
}

nextZone() {
	global
	if (gameMode != "FARMING") {
		clickPos(xPlusOneZone, yZone)
		sleep % zzz
	}
}

timeBetweenZones(z1, z2) {
	global
	local currentTime := getTime()
	local z1time := zoneTime.HasKey(z1) ? zoneTime[z1] : currentTime
	local z2time := zoneTime.HasKey(z2) ? zoneTime[z2] : currentTime
	return abs(z1time - z2time)
}

storeZoneTime() {
	global
	local cz := getCurrentZone()
	if (cz > 0) {
		local pz := cz - 1
		local currentTime := getTime()

		if (!zoneTime.HasKey(pz)) {
			zoneTime[pz] := currentTime
		}
		if (!zoneTime.HasKey(cz)) {
			zoneTime[cz] := currentTime
			; Reached a new non-boss zone?
			if (gameMode != "PROGRESSING" and mod(cz, 5) > 0 and pz > zoneTime.MinIndex()) {
				; Yup!
				setGameMode("PROGRESSING")
			}
			bossFailCounter := 0
		}
	}
}

logZoneData(zStart, zEnd, zInterval) {
	global
	local startZone := zStart < zoneTime.MinIndex() ? zoneTime.MinIndex() : zStart
	local endZone := zEnd > zoneTime.MaxIndex() ? zoneTime.MaxIndex() : zEnd
	local intervals := ceil((endZone - startZone) / zInterval)

	if (startZone >= endZone) {
		return 0
	}

	local zone := startZone
	local prevZone := zone - zInterval

	local totalTime := 0
	local intervalTime := 0

	local t := "`t" ; tab
	local nl := "`n" ; new line
	local zoneData := "Zones: " . startZone . " -> " . endZone . ", Interval: " . zInterval
	zoneData .= nl . "Zone" . t . "Time" . t . "Diff (s)"
	zoneData .= nl . zone . t . "00:00:00" . t . "0"

	loop % intervals {
		zone += zInterval
		if (zone > endZone) {
			zone := endZone
		}
		prevZone += zInterval

		totalTime := timeBetweenZones(startZone, zone)
		intervalTime := timeBetweenZones(prevZone, zone)

		zoneData .= nl . zone . t . formatSeconds(totalTime) . t . round(intervalTime, 1)
	}

	logger(zoneData, "INFO", "_zone_data")
}

startFarming() {
	global
	if (gameMode != "FARMING") {
		setGameMode("FARMING")
		setFarmMode()
		scrollToZone(zoneTime.MaxIndex())
		local farmZone := getCurrentZone()
		if (mod(farmZone, 5) = 0) {
			scrollToZone(--farmZone)
		}
		; Farm on highest recorded non-boss zone
		showDebugSplash("Farm @ Lvl " . farmZone . " for " . farmTime . "s")
		SetTimer, farmTimer, % -farmTime * 1000
	}
}

bossFight() {
	global
	if (gameMode != "FIGHTING") {
		setGameMode("FIGHTING")
		setProgressionMode()
		SetTimer, bossTimer, % -bossFightTimer * 1000, 2 ; prio 2
	}
}

startTimer() {
	global
	SetTimer, timeTick, 250, 4 ; prio 4
}

stopTimer() {
	global
	SetTimer, timeTick, off
}

getTime() {
	global
	return elapsedTime
}

getCurrentZone() {
	global
	local zone
	; Try get the current zone lvl
	loop 2 {
		zone := getZone()
		if (zone > 0) {
			return zone
		}
		sleep % zzz
	}
	showTraceSplash("Failed to get current zone lvl!")
	return zoneTime.MaxIndex()
}

; -----------------------------------------------------------------------------------------
; -- Subroutines
; -----------------------------------------------------------------------------------------

timeTick:
	elapsedTime += 0.25
return

; Safety zone around the in-game tabs (that triggers an automatic script pause when breached)
checkMousePosition:
	checkSafetyZones()
return

checkWindowVisibility:
	if (!locateImage(imgSmile)) {
		WinActivate, ahk_id %chWinId%
	}
return

comboTimer:
	activateSkills(skillCombo[comboIndex])
	comboIndex := comboIndex < skillCombo.MaxIndex() ? comboIndex+1 : 2
return

clickerStopTimer:
	clickerStop()
return

farmTimer:
	if (gameMode != "PROGRESSING") {
		bossFight()
	}
return

bossTimer:
	if (!locateImage(imgProgression)) {
		; Failed the boss, back to farming...
		showDebugSplash("Failed boss!")
		startFarming()
	}
return

ascendTimer:
	readyToAscend := true
return

zoneTimer:
	storeZoneTime()
return

nextZoneTimer:
	nextZone()
return


[excel] Add save as functionality

[excel] Add save as functionality

; Autohotkey configuration file
; AHK Version v.1.1.32.00
; Franklin Chou (franklin.chou@nelsonmullins.com)
; 22 Mar. 2020
; Tested to work on Lenovo X1 Yoga, Gen. 4

#SingleInstance Force
#Persistent

GLOBAL_DEBUG_MODE := 0
Return

CapsLock::Ctrl
LCtrl::Return
;LCtrl::CapsLock

; DEBUG TOOLS -----------------------------------------------------------------

#c::
  If(GLOBAL_DEBUG_MODE > 0) {
    ; WinGetTitle, title, A
    WinGetClass, title, A
    MsgBox %title%
  }
Return

f12::
  If(GLOBAL_DEBUG_MODE > 0) {
    Reload
  }
Return

#'::
  If(GLOBAL_DEBUG_MODE > 0) {
    WinGet, windows, List
    Loop %windows% {
      id := windows%A_Index%
      WinGetTitle wt, ahk_id %id%
      r .= wt . "`n"
    }
    MsgBox %r%
    r := ""
  }
Return

; TIMEKEEPER ------------------------------------------------------------------

; Shift focus to the timekeeper app
#!e::
  timekeeper_app := WinExist("ahk_class WindowsForms10.Window.8.app.0.262fb3d")
  ;MsgBox %timekeeper_app%
  If(timekeeper_app > 0) {
    WinActivate, ahk_id %timekeeper_app%
  } Else {
    Run, DTE.exe, C:\Program Files (x86)\DTEAxiom
  }
Return

; Start the timer on the selected item in the daily view
#IfWinActive ahk_class WindowsForms10.Window.8.app.0.262fb3d
  >+s::
    WinGetActiveTitle, active_title
    match_result := RegExMatch(active_title, "^Entries for")
    ; each item in the dropdown is approximately 25 pixels
    ; so item 4 (Start Timer) is 100 pixels down from the context menu
    drop_down := 100
    If(match_result > 0) {
      WinMove, 100, 100
      MouseMove, 100, 280
      Send {RButton}
      Sleep, 10 ; need to sleep to allow submenu time to come up
      MouseMove, 170, 300 + drop_down
      Send {LButton}
    }
Return

; MSOFFICE --------------------------------------------------------------------

; Let the print screen key open the application options key (for spell check)
#If WinActive("ahk_class OpusApp") || WinActive("ahk_class rctrl_renwnd32")
  PrintScreen::AppsKey
Return

; This ridiculous keymapping is thanks to Lenovo keyboard manager
; which maps F12 to some bloatware keyboard manager utility
#If WinActive("ahk_class OpusApp") || WinActive("ahk_class XLMAIN")
  F12::
    Send {F12}
Return

; WORD ONLY -------------------------------------------------------------------

; Cycle through types of markup
#If WinActive("ahk_class OpusApp")
  >+m::
    Send !r
    Sleep, 10
    Send td
    Send {Down}
    Send {Enter}
Return

; Autohotkey configuration file
; AHK Version v.1.1.32.00
; Franklin Chou (franklin.chou@nelsonmullins.com)
; 22 Mar. 2020
; Tested to work on Lenovo X1 Yoga, Gen. 4

#SingleInstance Force
#Persistent

GLOBAL_DEBUG_MODE := 0
Return

CapsLock::Ctrl
LCtrl::Return
;LCtrl::CapsLock

; DEBUG TOOLS -----------------------------------------------------------------

#c::
  If(GLOBAL_DEBUG_MODE > 0) {
    ; WinGetTitle, title, A
    WinGetClass, title, A
    MsgBox %title%
  }
Return

f12::
  If(GLOBAL_DEBUG_MODE > 0) {
    Reload
  }
Return

#'::
  If(GLOBAL_DEBUG_MODE > 0) {
    WinGet, windows, List
    Loop %windows% {
      id := windows%A_Index%
      WinGetTitle wt, ahk_id %id%
      r .= wt . "`n"
    }
    MsgBox %r%
    r := ""
  }
Return

; TIMEKEEPER ------------------------------------------------------------------

; Shift focus to the timekeeper app
#!e::
  timekeeper_app := WinExist("ahk_class WindowsForms10.Window.8.app.0.262fb3d")
  ;MsgBox %timekeeper_app%
  If(timekeeper_app > 0) {
    WinActivate, ahk_id %timekeeper_app%
  } Else {
    Run, DTE.exe, C:\Program Files (x86)\DTEAxiom
  }
Return

; Start the timer on the selected item in the daily view
#IfWinActive ahk_class WindowsForms10.Window.8.app.0.262fb3d
  >+s::
    WinGetActiveTitle, active_title
    match_result := RegExMatch(active_title, "^Entries for")
    ; each item in the dropdown is approximately 25 pixels
    ; so item 4 (Start Timer) is 100 pixels down from the context menu
    drop_down := 100
    If(match_result > 0) {
      WinMove, 100, 100
      MouseMove, 100, 280
      Send {RButton}
      Sleep, 10 ; need to sleep to allow submenu time to come up
      MouseMove, 170, 300 + drop_down
      Send {LButton}
    }
Return

; WORD/OUTLOOK ----------------------------------------------------------------

; Let the print screen key open the application options key (for spell check)
#If WinActive("ahk_class OpusApp") || WinActive("ahk_class rctrl_renwnd32")
  PrintScreen::AppsKey
Return

; WORD ONLY -------------------------------------------------------------------

; Cycle through types of markup
#If WinActive("ahk_class OpusApp")
  >+m::
    Send !r
    Sleep, 10
    Send td
    Send {Down}
    Send {Enter}
Return

; This ridiculous keymapping is thanks to Lenovo keyboard manager
; which maps F12 to some bloatware keyboard manager utility
#If WinActive("ahk_class OpusApp")
  F12::
    Send {F12}
Return


Fixed folder creation for win8

Fixed folder creation for win8

;Control key Ctrl- ^
;Alt key Alt- !
;Shift key Shift- +
;Windows key Win- #

#Persistent
;#NoTrayIcon
#SingleInstance Force
DetectHiddenWindows, On
DetectHiddenText, On
SetTitleMatchMode, RegEx ;match titles and classes using regular expressions
SetTitleMatchMode Slow ;match titles and classes using regular expressions
CoordMode, Mouse, Screen
CoordMode, ToolTip, Screen
OldTicks = 0

GroupAdd, adobe, ahk_class OWL.DocumentWindow
GroupAdd, adobe, ahk_class illustrator
GroupAdd, adobe, ahk_class PSViewC
GroupAdd, adobe, ahk_class Photoshop
GroupAdd, adobe, Photoshop
return

RandomFileName()
{
	FormatTime, fileName,, yyyyMMdd_HHmmss_
	characters := "abcdefghijklmnopqrstuvwxyz"
	Loop 3
	{
		Random, rand, 1, strlen(characters)
		fileName := fileName . substr(characters, rand, 1)
	}
	return fileName
}

UploadFiles()
{
	ToolTip, Uploading files..., -1920+50, 50
	RunWait, C:\Program Files (x86)\cygwin\bin\mintty.exe /bin/bash -l -c send_to_pinkyard.py|clip, C:\Program Files (x86)\cygwin
	ToolTip
	return
}

MakeScreen(arguments)
{
	baseFileName := "Z:\hub\pinkyard\queued\" . RandomFileName()
	PNGFileName := "Z:\tmp.png"
	JPGFileName := "Z:\tmp.jpg"
	RunWait, % "C:\program files (x86)\scrsh.exe --path " . PNGFileName . " " . arguments, "Z:\", Hide
	if %ErrorLevel% = 0
	{
		RunWait, % "Z:\src\optipng\support\optipng.exe -o1 " . PNGFileName, "Z:\", Hide
		RunWait, % "C:\Program Files (x86)\cygwin\bin\convert.exe " . PNGFileName . " -quality 80 " . JPGFileName, "Z:\", Hide
		FileGetSize, JPGFileSize, % JPGFileName
		FileGetSize, PNGFileSize, % PNGFileName
		if % PNGFileSize > JPGFileSize
		{
			path1 := JPGFileName
			path2 := baseFileName . ".jpg"
		}
		else
		{
			path1 := PNGFileName
			path2 := baseFileName . ".png"
		}
		FileCopy, % path1, % path2
		FileDelete, % JPGFileName
		FileDelete, % PNGFileName
		SoundPlay, Z:\clutter\sounds\ping.wav
	}
	return path
}

OnClipboardChange:
	if (A_EventInfo == 1) and (SubStr(clipboard, 1, 7) == "http://")
	{
		StringReplace, clipboard, clipboard, `%5b, [, All
		StringReplace, clipboard, clipboard, `%5d, ], All
	}
	return

FRun(window, path, folder)
{
	if (!window or !WinExist(window))
	{
		Run, %path%, %folder%
	}
	else
	{
		WinShow
		WinActivate
	}
	return
}

;programs
#E::FRun("ahk_class CabinetWClass", "Z:\", "Z:\") ;explorer
Launch_Media::FRun("ahk_class {97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}", "C:\program files (x86)\foobar2000\foobar2000.exe", "C:\Program Files (x86)\foobar2000") ;foobar

+^!U::UploadFiles() ;upload files
+^M::
	if (WinExist("ahk_class TfrmSend")) {
		WinShow
		WinActivate
	}
	return

+^!M::
	if (!WinExist("^WTW")) {
		;RunWait, taskkill /im miranda32.exe /f
		Run, "C:\Program Files\WTW\WTW.exe" -x, "C:\Program Files\WTW"
	} else {
		WinShow
		WinActivate
	}
	return

+^![::FRun(false, "C:\Program Files (x86)\cygwin\bin\mintty.exe -", "C:\Program Files (x86)\cygwin\bin") ;cygwin
 ^![::
	if (!WinExist("ahk_class mintty_thetty")) {
		Run, "C:\Program Files (x86)\cygwin\bin\mintty.exe" --size "120`,40" --position "1050`,45" --class "mintty_thetty" --exec "/bin/bash" --login -i -c 'while /bin/true; do /bin/bash; cygstart "%A_AhkPath%" "Z:\src\hide.ahk"; clear; done', "C:/program files(x86)/cygwin/bin"
		WinWait ahk_class mintty_thetty
	} else {
		WinShow
		WinActivate
	}
	WinSet, Style, -0x004b0000
	return

; putty @burza - activate/run + fix screen position
+^!]::
	Run, "C:\Program Files (x86)\putty\putty.exe" -load burza, "C:\Program Files (x86)"
	WinWaitActive ahk_class PuTTY
	WinMove, , , 100, 100
	return
 ^!]::
	if (!WinExist("ahk_class PuTTY")) {
		Run, "C:\Program Files (x86)\putty\putty.exe" -load burza, "C:\Program Files (x86)"
		WinWait ahk_class PuTTY
		WinMove, , , 100, 100
	} else {
		WinShow
		WinActivate
	}
	return
 ^!/::FRun("CDisplayEx", "C:\Program Files (x86)\CDisplayEx\cdisplayex.exe", "Z:\") ;cdisplay
 ^!;::
	IfWinExist, Mozilla Firefox
	{
		WinShow
		WinActivate
		Send, !d
	}
	else
	{
		Run, "C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
	}
	return

+^!N:: ;text editor - activate/run + fix screen position
	IfWinExist, ahk_class Vim
	{
		WinShow
		WinActivate
	} else {
		Run, "C:\Program Files (x86)\cygwin\bin\mintty.exe" --size "240`,90" --position "100`,45" --class "Vim" --exec "vim" --cmd 'cd /cygdrive/z', "C:/program files(x86)/cygwin/bin"
		WinWait, ahk_class Vim
	}
	WinMove, , , 100, 100, 1600, 900
	return

;windows powershell / cmd functionalities
#IfWinActive ahk_class ConsoleWindowClass
^V::SendInput {Raw}%clipboard% ;paste on ctrl+v
^!PgUp::Send ! el{PgUp}{Enter} ;scroll on ctrl+page up
^!PgDn::Send ! el{PgDn}{Enter} ;scroll on ctrl+page down
#IfWinActive

;acdsee functionalities
#IfWinActive ahk_class ACDBrowser
	WheelDown::Send, ^{Down}^{Space}
	WheelUp::Send, ^{Up}^{Space}
#IfWinActive
#IfWinActive ACDSee
	WheelDown::
		if (A_TickCount - OldTicks >= 80) {
			Send, {WheelDown}
			OldTicks := A_TickCount
		}
		return
	]:: ;send to document folder
	RButton & ~LButton::
		FormatTime, Folder,, 'Z:\'
		Send {LAlt down}c
		Send {LAlt up}
		SendInput %Folder%
		Send {Return}
		return
	[:: ;send to upload folder
	~LButton & RButton::
		Send {LAlt down}c
		Send {LAlt up}
		SendInput Z:\hub\pinkyard\queued
		Send {Return}
		Sleep 100
		return
	XButton1::
		WinGet, Transparency, Transparent, A
		if ! Transparency
			WinSet, Transparent, 255, A
		else if Transparency = 255
			WinSet, Transparent, 0, A
		return
	XButton2:: ;start slideshow
		Send {Pause}
		return
#IfWinActive

;other functionalities
!F12::Reload
!M:: ;maximize active window
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf030 ;WM_SYSCOMMAND, SC_MAXIMIZE
	return
!R:: ;restore active window
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf120 ;WM_SYSCOMMAND, SC_RESTORE
	return
!W:: ;minimize active window
	if WinActive("WTW$") {
		WinHide
	} else if WinActive("ahk_class OperaWindowClass") {
		SendInput +!^h
	} else {
		ActiveHwnd := WinExist("A")
		PostMessage 0x112, 0xF020 ;WM_SYSCOMMAND, SC_MINIMIZE
	}
	return
!A::WinSet, AlwaysOnTop, Toggle, A

;transparency
#1::WinSet, Transparent, 0, A
#2::WinSet, Transparent, 64, A
#3::WinSet, Transparent, 255, A
#WheelUp::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency + 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return
#WheelDown::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency - 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return

 +PrintScreen::MakeScreen("-g")
 !PrintScreen::MakeScreen("-r active-window --shift -8,-8,+16,+16 --force-gui")
!#PrintScreen::MakeScreen("-r active-monitor")
 #PrintScreen::MakeScreen("-r all-monitors")

;screenshots [media player classic]
#IfWinActive ahk_class MediaPlayerClassicW
[::
	Send {F5}
	return
#IfWinActive

;explorer
#IfWinActive ahk_class CabinetWClass
	;new folder
	^n::
		Send {AppsKey}wf
		return
#IfWinActive

#IfWinActive ahk_class PuTTY
	;putty ctrl+alt+pageup/pagedown
	^!PgUp::SendInput {Escape}:tabprev{Return}
	^!PgDn::SendInput {Escape}:tabnext{Return}
	;putty pasting
	+Numpad0::
	+NumpadIns::
	+Insert::
		WinGetPos, winX, winY, winW, winH
		tempX := winX+(winW//2)
		tempY := winY+(winH//2)
		MouseClick, Right, %tempX%, %tempY%
		return
#IfWinActive

#IfWinActive ahk_group adobe
	~LAlt::return
	LAlt UP::Send {Escape}
#IfWinActive

;Control key Ctrl- ^
;Alt key Alt- !
;Shift key Shift- +
;Windows key Win- #

#Persistent
;#NoTrayIcon
#SingleInstance Force
DetectHiddenWindows, On
DetectHiddenText, On
SetTitleMatchMode, RegEx ;match titles and classes using regular expressions
SetTitleMatchMode Slow ;match titles and classes using regular expressions
CoordMode, Mouse, Screen
CoordMode, ToolTip, Screen
OldTicks = 0

GroupAdd, adobe, ahk_class OWL.DocumentWindow
GroupAdd, adobe, ahk_class illustrator
GroupAdd, adobe, ahk_class PSViewC
GroupAdd, adobe, ahk_class Photoshop
GroupAdd, adobe, Photoshop
return

RandomFileName()
{
	FormatTime, fileName,, yyyyMMdd_HHmmss_
	characters := "abcdefghijklmnopqrstuvwxyz"
	Loop 3
	{
		Random, rand, 1, strlen(characters)
		fileName := fileName . substr(characters, rand, 1)
	}
	return fileName
}

UploadFiles()
{
	ToolTip, Uploading files..., -1920+50, 50
	RunWait, C:\Program Files (x86)\cygwin\bin\mintty.exe /bin/bash -l -c send_to_pinkyard.py|clip, C:\Program Files (x86)\cygwin
	ToolTip
	return
}

MakeScreen(arguments)
{
	baseFileName := "Z:\hub\pinkyard\queued\" . RandomFileName()
	PNGFileName := "Z:\tmp.png"
	JPGFileName := "Z:\tmp.jpg"
	RunWait, % "C:\program files (x86)\scrsh.exe --path " . PNGFileName . " " . arguments, "Z:\", Hide
	if %ErrorLevel% = 0
	{
		RunWait, % "Z:\src\optipng\support\optipng.exe -o1 " . PNGFileName, "Z:\", Hide
		RunWait, % "C:\Program Files (x86)\cygwin\bin\convert.exe " . PNGFileName . " -quality 80 " . JPGFileName, "Z:\", Hide
		FileGetSize, JPGFileSize, % JPGFileName
		FileGetSize, PNGFileSize, % PNGFileName
		if % PNGFileSize > JPGFileSize
		{
			path1 := JPGFileName
			path2 := baseFileName . ".jpg"
		}
		else
		{
			path1 := PNGFileName
			path2 := baseFileName . ".png"
		}
		FileCopy, % path1, % path2
		FileDelete, % JPGFileName
		FileDelete, % PNGFileName
		SoundPlay, Z:\clutter\sounds\ping.wav
	}
	return path
}

OnClipboardChange:
	if (A_EventInfo == 1) and (SubStr(clipboard, 1, 7) == "http://")
	{
		StringReplace, clipboard, clipboard, `%5b, [, All
		StringReplace, clipboard, clipboard, `%5d, ], All
	}
	return

FRun(window, path, folder)
{
	if (!window or !WinExist(window))
	{
		Run, %path%, %folder%
	}
	else
	{
		WinShow
		WinActivate
	}
	return
}

;programs
#E::FRun("ahk_class CabinetWClass", "Z:\", "Z:\") ;explorer
Launch_Media::FRun("ahk_class {97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}", "C:\program files (x86)\foobar2000\foobar2000.exe", "C:\Program Files (x86)\foobar2000") ;foobar

+^!U::UploadFiles() ;upload files
+^M::
	if (WinExist("ahk_class TfrmSend")) {
		WinShow
		WinActivate
	}
	return

+^!M::
	if (!WinExist("^WTW")) {
		;RunWait, taskkill /im miranda32.exe /f
		Run, "C:\Program Files\WTW\WTW.exe" -x, "C:\Program Files\WTW"
	} else {
		WinShow
		WinActivate
	}
	return

+^![::FRun(false, "C:\Program Files (x86)\cygwin\bin\mintty.exe -", "C:\Program Files (x86)\cygwin\bin") ;cygwin
 ^![::
	if (!WinExist("ahk_class mintty_thetty")) {
		Run, "C:\Program Files (x86)\cygwin\bin\mintty.exe" --size "120`,40" --position "1050`,45" --class "mintty_thetty" --exec "/bin/bash" --login -i -c 'while /bin/true; do /bin/bash; cygstart "%A_AhkPath%" "Z:\src\hide.ahk"; clear; done', "C:/program files(x86)/cygwin/bin"
		WinWait ahk_class mintty_thetty
	} else {
		WinShow
		WinActivate
	}
	WinSet, Style, -0x004b0000
	return

; putty @burza - activate/run + fix screen position
+^!]::
	Run, "C:\Program Files (x86)\putty\putty.exe" -load burza, "C:\Program Files (x86)"
	WinWaitActive ahk_class PuTTY
	WinMove, , , 100, 100
	return
 ^!]::
	if (!WinExist("ahk_class PuTTY")) {
		Run, "C:\Program Files (x86)\putty\putty.exe" -load burza, "C:\Program Files (x86)"
		WinWait ahk_class PuTTY
		WinMove, , , 100, 100
	} else {
		WinShow
		WinActivate
	}
	return
 ^!/::FRun("CDisplayEx", "C:\Program Files (x86)\CDisplayEx\cdisplayex.exe", "Z:\") ;cdisplay
 ^!;::
	IfWinExist, Mozilla Firefox
	{
		WinShow
		WinActivate
		Send, !d
	}
	else
	{
		Run, "C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
	}
	return

+^!N:: ;text editor - activate/run + fix screen position
	IfWinExist, ahk_class Vim
	{
		WinShow
		WinActivate
	} else {
		Run, "C:\Program Files (x86)\cygwin\bin\mintty.exe" --size "240`,90" --position "100`,45" --class "Vim" --exec "vim" --cmd 'cd /cygdrive/z', "C:/program files(x86)/cygwin/bin"
		WinWait, ahk_class Vim
	}
	WinMove, , , 100, 100, 1600, 900
	return

;windows powershell / cmd functionalities
#IfWinActive ahk_class ConsoleWindowClass
^V::SendInput {Raw}%clipboard% ;paste on ctrl+v
^!PgUp::Send ! el{PgUp}{Enter} ;scroll on ctrl+page up
^!PgDn::Send ! el{PgDn}{Enter} ;scroll on ctrl+page down
#IfWinActive

;acdsee functionalities
#IfWinActive ahk_class ACDBrowser
	WheelDown::Send, ^{Down}^{Space}
	WheelUp::Send, ^{Up}^{Space}
#IfWinActive
#IfWinActive ACDSee
	WheelDown::
		if (A_TickCount - OldTicks >= 80) {
			Send, {WheelDown}
			OldTicks := A_TickCount
		}
		return
	]:: ;send to document folder
	RButton & ~LButton::
		FormatTime, Folder,, 'Z:\'
		Send {LAlt down}c
		Send {LAlt up}
		SendInput %Folder%
		Send {Return}
		return
	[:: ;send to upload folder
	~LButton & RButton::
		Send {LAlt down}c
		Send {LAlt up}
		SendInput Z:\hub\pinkyard\queued
		Send {Return}
		Sleep 100
		return
	XButton1::
		WinGet, Transparency, Transparent, A
		if ! Transparency
			WinSet, Transparent, 255, A
		else if Transparency = 255
			WinSet, Transparent, 0, A
		return
	XButton2:: ;start slideshow
		Send {Pause}
		return
#IfWinActive

;other functionalities
!F12::Reload
!M:: ;maximize active window
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf030 ;WM_SYSCOMMAND, SC_MAXIMIZE
	return
!R:: ;restore active window
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf120 ;WM_SYSCOMMAND, SC_RESTORE
	return
!W:: ;minimize active window
	if WinActive("WTW$") {
		WinHide
	} else if WinActive("ahk_class OperaWindowClass") {
		SendInput +!^h
	} else {
		ActiveHwnd := WinExist("A")
		PostMessage 0x112, 0xF020 ;WM_SYSCOMMAND, SC_MINIMIZE
	}
	return
!A::WinSet, AlwaysOnTop, Toggle, A

;transparency
#1::WinSet, Transparent, 0, A
#2::WinSet, Transparent, 64, A
#3::WinSet, Transparent, 255, A
#WheelUp::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency + 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return
#WheelDown::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency - 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return

 +PrintScreen::MakeScreen("-g")
 !PrintScreen::MakeScreen("-r active-window --shift -8,-8,+16,+16 --force-gui")
!#PrintScreen::MakeScreen("-r active-monitor")
 #PrintScreen::MakeScreen("-r all-monitors")

;screenshots [media player classic]
#IfWinActive ahk_class MediaPlayerClassicW
[::
	Send {F5}
	return
#IfWinActive

;explorer
#IfWinActive ahk_class CabinetWClass
	;new folder
	^n::
		Send {Alt down}f{Alt up}wf
		return
#IfWinActive

#IfWinActive ahk_class PuTTY
	;putty ctrl+alt+pageup/pagedown
	^!PgUp::SendInput {Escape}:tabprev{Return}
	^!PgDn::SendInput {Escape}:tabnext{Return}
	;putty pasting
	+Numpad0::
	+NumpadIns::
	+Insert::
		WinGetPos, winX, winY, winW, winH
		tempX := winX+(winW//2)
		tempY := winY+(winH//2)
		MouseClick, Right, %tempX%, %tempY%
		return
#IfWinActive

#IfWinActive ahk_group adobe
	~LAlt::return
	LAlt UP::Send {Escape}
#IfWinActive


Removed smart compression from screenshots

;Control key Ctrl- ^
;Alt key Alt- !
;Shift key Shift- +
;Windows key Win- #

#Persistent
;#NoTrayIcon
#SingleInstance Force
DetectHiddenWindows, On
DetectHiddenText, On
SetTitleMatchMode, RegEx ;match titles and classes using regular expressions
SetTitleMatchMode Slow ;match titles and classes using regular expressions
CoordMode, Mouse, Screen
CoordMode, ToolTip, Screen
OldTicks = 0

;check for cygwin path
global CygPath
CygPath = C:\Program Files (x86)\cygwin
IfNotExist %CygPath%
	CygPath = C:\Cygwin

global BrowserPath
BrowserPath = C:\Program Files (x86)\Mozilla Firefox\firefox.exe
IfNotExist %BrowserPath%
	BrowserPath = "C:\Users\rr-\AppData\Local\Google\Chrome\Application\chrome.exe"

global WTWPath
WTWPath = C:\Program Files\WTW\WTW.exe
IfNotExist %WTWPath%
	WTWPath = C:\Program Files\K2T\WTW\WTW.exe

global MonitorWorkAreaLeft
global MonitorWorkAreaRight
global MonitorWorkAreaTop
global MonitorWorkAreaBottom
SysGet, MonitorPrimary, MonitorPrimary
SysGet, MonitorWorkArea, MonitorWorkArea, %MonitorPrimary%

GroupAdd, adobe, ahk_class OWL.DocumentWindow
GroupAdd, adobe, ahk_class illustrator
GroupAdd, adobe, ahk_class PSViewC
GroupAdd, adobe, ahk_class Photoshop
GroupAdd, adobe, Photoshop

return

;replace stuff in clipboard when it changes
OnClipboardChange:
{
	if (A_EventInfo == 1) and (SubStr(clipboard, 1, 7) == "http://")
	{
		StringReplace, clipboard, clipboard, [, `%5b, All
		StringReplace, clipboard, clipboard, \, `%5c, All
		StringReplace, clipboard, clipboard, ], `%5d, All
	}
	return
}

;declare some fun functions...

;activate/run a program
FRun(window, path, folder)
{
	if (!window or !WinExist(window))
	{
		Run, %path%, %folder%
	}
	else
	{
		WinShow
		WinActivate
	}
	return
}

;create random file name
RandomFileName()
{
	FormatTime, fileName,, yyyyMMdd_HHmmss_
	characters := "abcdefghijklmnopqrstuvwxyz"
	Loop 3
	{
		Random, rand, 1, strlen(characters)
		fileName := fileName . substr(characters, rand, 1)
	}
	return fileName
}

;calling screen script
MakeScreen(arguments)
{
	baseFileName := "Z:\" . RandomFileName() . ".png"
	RunWait, % "Z:\software\utilities\scrsh.exe --path " . baseFileName . " " . arguments, "Z:\", Hide
	SoundPlay, Z:\clutter\sounds\ping.wav
	return baseFileName
}

#If FileExist(CygPath)
	;cygwin - activate/run
	+^![::
	{
		X := MonitorWorkAreaRight - 920
		Y := 45
		Run, %CygPath%\bin\mintty.exe --title "Terminal" --size "120`,40" --position "%X%`,%Y%" --class "mintty_thetty" --exec /bin/bash -l, %CygPath%
		return
	}
	^![::
	{
		if (!WinExist("ahk_class mintty_thetty"))
		{
			X := MonitorWorkAreaRight - 920
			Y := 45
			Run, %CygPath%\bin\mintty.exe --title "Terminal" --size "120`,40" --position "%X%`,%Y%" --class "mintty_thetty" --exec /bin/bash -l -c 'while /bin/true`; do /bin/bash`; cygstart "%A_AhkPath%" "Z:\src\scripts\hide.ahk"`; clear; done', %CygPath%
			WinWait ahk_class mintty_thetty
		}
		else
		{
			WinShow
			WinActivate
		}
		return
	}
	;ssh to burza - activate/run
	+^!]::
	{
		X := 45
		Y := 45
		Run, %CygPath%\bin\mintty.exe --title "Remote terminal" --size "200`,70" --position "%X%`,%Y%" --class mintty_ssh --exec /bin/ssh -p 65000 rr-@sakuya.pl -t /bin/bash -i, %CygPath%/bin
		return
	}
	^!]::
	{
		X := 45
		Y := 45
		if (!WinExist("ahk_class mintty_ssh"))
		{
			Run, %CygPath%\bin\mintty.exe --title "Remote terminal" --size "200`,70" --position "%X%`,%Y%" --class mintty_ssh --exec /bin/ssh -p 65000 rr-@sakuya.pl, %CygPath%/bin
			WinWait ahk_class mintty_ssh
		}
		else
		{
			WinShow
			WinActivate
		}
		return
	}
	;text editor - activate/run + fix screen position
	+^!N::
	{
		IfWinExist, ahk_class Vim
		{
			WinShow
			WinActivate
		}
		else
		{
			Run, %CygPath%\bin\mintty.exe --size "240`,90" --position "100`,45" --class "Vim" --exec /bin/bash -l -c /bin/vim, %CygPath%/bin
			WinWait, ahk_class Vim
		}
		WinMove, , , 100, 100, 1600, 900
		return
	}
	+PrintScreen::MakeScreen("-g")
	!PrintScreen::MakeScreen("-r active-window --shift -8,-8,+16,+16 --force-gui")
	!#PrintScreen::MakeScreen("-r active-monitor")
	#PrintScreen::MakeScreen("-r all-monitors")

;explorer - activate/run
#E::FRun("ahk_class CabinetWClass", "Z:\", "Z:\")

;foobar - activate/run
#If FileExist("C:\program files (x86)\foobar2000\foobar2000.exe")
	Launch_Media::FRun("ahk_class {97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}", "C:\program files (x86)\foobar2000\foobar2000.exe", "C:\Program Files (x86)\foobar2000")

;wtw
#If FileExist(WTWPath)
	;wtw - activate/run
	+^!M::
		if (!WinExist("^WTW"))
		{
			Run, %WTWPath% -x
		}
		else
		{
			WinShow
			WinActivate
		}
		return

;cdisplayex - activate/run
#If FileExist("C:\Program Files (x86)\CDisplayEx\cdisplayex.exe")
	^!/::FRun("CDisplayEx", "C:\Program Files (x86)\CDisplayEx\cdisplayex.exe", "Z:\")

;browser
#If FileExist(BrowserPath)
	;web browser - activate/run
	^!;::
		if WinExist("Mozilla Firefox")
		{
			WinShow
			WinActivate
			Send, !d
		}
		else if WinExist("ahk_class Chrome_WidgetWin_1")
		{
			WinActivate
			ControlFocus, Chrome_OmniboxView1
			SendInput, {Home}+{End}
		}
		else
		{
			IfExist %BrowserPath%
				Run, %BrowserPath%
		}
		return

;windows powershell / cmd functionalities
#IfWinActive ahk_class ConsoleWindowClass
	^V::SendInput {Raw}%clipboard% ;paste on ctrl+v
	^!PgUp::Send ! el{PgUp}{Enter} ;scroll on ctrl+page up
	^!PgDn::Send ! el{PgDn}{Enter} ;scroll on ctrl+page down
#IfWinActive

;acdsee functionalities
#IfWinActive ahk_class ACDBrowser
	WheelDown::Send, ^{Down}^{Space}
	WheelUp::Send, ^{Up}^{Space}
	^!X::
		SendInput {Left 10}{Shift down}{Right 10}{Shift up}
		return
#IfWinActive
#IfWinActive ACDSee
	WheelDown::
		if (A_TickCount - OldTicks >= 80)
		{
			Send, {WheelDown}
			OldTicks := A_TickCount
		}
		return
	]:: ;send to document folder
	RButton & ~LButton::
		FormatTime, Folder,, 'Z:\'
		Send {LAlt down}c
		Send {LAlt up}
		SendInput %Folder%
		Send {Return}
		return
	[:: ;send to upload folder
	~LButton & RButton::
		Send {LAlt down}c
		Send {LAlt up}
		SendInput Z:\hub\yume.pl
		Send {Return}
		Sleep 100
		return
	XButton1::
		WinGet, Transparency, Transparent, A
		if ! Transparency
			WinSet, Transparent, 255, A
		else if Transparency = 255
			WinSet, Transparent, 0, A
		return
	XButton2:: ;start slideshow
		Send {Pause}
		return
#IfWinActive

;other functionalities

;autohotkey - reload this script
!F11::
{
	Process, Close, Rainmeter.exe
	Run, C:\Program Files \Rainmeter\Rainmeter.exe
	return
}
!F12::Reload

;maximize active window
!M::
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf030 ;WM_SYSCOMMAND, SC_MAXIMIZE
	return

;restore active window
!R::
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf120 ;WM_SYSCOMMAND, SC_RESTORE
	return

;minimize active window
!W::
	if WinActive("WTW$")
	{
		WinHide
	}
	else
	{
		ActiveHwnd := WinExist("A")
		PostMessage 0x112, 0xF020 ;WM_SYSCOMMAND, SC_MINIMIZE
	}
	return

;always on top
!A::WinSet, AlwaysOnTop, Toggle, A

;transparency
#1::WinSet, Transparent, 0, A
#2::WinSet, Transparent, 64, A
#3::WinSet, Transparent, 255, A
#WheelUp::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency + 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return
#WheelDown::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency - 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return

;explorer
#IfWinActive ahk_class CabinetWClass
	;new folder
	^n::
		Send {AppsKey}wf
		return
#IfWinActive

;adobe products
#IfWinActive ahk_group adobe
	~LAlt::return
	LAlt UP::Send {Escape}
#IfWinActive

;Control key Ctrl- ^
;Alt key Alt- !
;Shift key Shift- +
;Windows key Win- #

#Persistent
;#NoTrayIcon
#SingleInstance Force
DetectHiddenWindows, On
DetectHiddenText, On
SetTitleMatchMode, RegEx ;match titles and classes using regular expressions
SetTitleMatchMode Slow ;match titles and classes using regular expressions
CoordMode, Mouse, Screen
CoordMode, ToolTip, Screen
OldTicks = 0

;check for cygwin path
global CygPath
CygPath = C:\Program Files (x86)\cygwin
IfNotExist %CygPath%
	CygPath = C:\Cygwin

global BrowserPath
BrowserPath = C:\Program Files (x86)\Mozilla Firefox\firefox.exe
IfNotExist %BrowserPath%
	BrowserPath = "C:\Users\rr-\AppData\Local\Google\Chrome\Application\chrome.exe"

global WTWPath
WTWPath = C:\Program Files\WTW\WTW.exe
IfNotExist %WTWPath%
	WTWPath = C:\Program Files\K2T\WTW\WTW.exe

global MonitorWorkAreaLeft
global MonitorWorkAreaRight
global MonitorWorkAreaTop
global MonitorWorkAreaBottom
SysGet, MonitorPrimary, MonitorPrimary
SysGet, MonitorWorkArea, MonitorWorkArea, %MonitorPrimary%

GroupAdd, adobe, ahk_class OWL.DocumentWindow
GroupAdd, adobe, ahk_class illustrator
GroupAdd, adobe, ahk_class PSViewC
GroupAdd, adobe, ahk_class Photoshop
GroupAdd, adobe, Photoshop

return

;replace stuff in clipboard when it changes
OnClipboardChange:
{
	if (A_EventInfo == 1) and (SubStr(clipboard, 1, 7) == "http://")
	{
		StringReplace, clipboard, clipboard, [, `%5b, All
		StringReplace, clipboard, clipboard, \, `%5c, All
		StringReplace, clipboard, clipboard, ], `%5d, All
	}
	return
}

;declare some fun functions...

;activate/run a program
FRun(window, path, folder)
{
	if (!window or !WinExist(window))
	{
		Run, %path%, %folder%
	}
	else
	{
		WinShow
		WinActivate
	}
	return
}

;create random file name
RandomFileName()
{
	FormatTime, fileName,, yyyyMMdd_HHmmss_
	characters := "abcdefghijklmnopqrstuvwxyz"
	Loop 3
	{
		Random, rand, 1, strlen(characters)
		fileName := fileName . substr(characters, rand, 1)
	}
	return fileName
}

;calling screen script
MakeScreen(arguments)
{
	baseFileName := "Z:\" . RandomFileName()
	PNGFileName := "Z:\tmp.png"
	JPGFileName := "Z:\tmp.jpg"
	RunWait, % "Z:\software\utilities\scrsh.exe --path " . PNGFileName . " " . arguments, "Z:\", Hide
	if %ErrorLevel% = 0
	{
		RunWait, % "Z:\src\optipng\support\optipng.exe -o1 " . PNGFileName, "Z:\", Hide
		RunWait, % CygPath . "\bin\convert.exe " . PNGFileName . " -quality 80 " . JPGFileName, "Z:\", Hide
		FileGetSize, JPGFileSize, % JPGFileName
		FileGetSize, PNGFileSize, % PNGFileName
		if % PNGFileSize > JPGFileSize
		{
			path1 := JPGFileName
			path2 := baseFileName . ".jpg"
		}
		else
		{
			path1 := PNGFileName
			path2 := baseFileName . ".png"
		}
		FileCopy, % path1, % path2
		FileDelete, % JPGFileName
		FileDelete, % PNGFileName
		SoundPlay, Z:\clutter\sounds\ping.wav
	}
	return path
}

#If FileExist(CygPath)
	;cygwin - activate/run
	+^![::
	{
		X := MonitorWorkAreaRight - 920
		Y := 45
		Run, %CygPath%\bin\mintty.exe --title "Terminal" --size "120`,40" --position "%X%`,%Y%" --class "mintty_thetty" --exec /bin/bash -l, %CygPath%
		return
	}
	^![::
	{
		if (!WinExist("ahk_class mintty_thetty"))
		{
			X := MonitorWorkAreaRight - 920
			Y := 45
			Run, %CygPath%\bin\mintty.exe --title "Terminal" --size "120`,40" --position "%X%`,%Y%" --class "mintty_thetty" --exec /bin/bash -l -c 'while /bin/true`; do /bin/bash`; cygstart "%A_AhkPath%" "Z:\src\scripts\hide.ahk"`; clear; done', %CygPath%
			WinWait ahk_class mintty_thetty
		}
		else
		{
			WinShow
			WinActivate
		}
		return
	}
	;ssh to burza - activate/run
	+^!]::
	{
		X := 45
		Y := 45
		Run, %CygPath%\bin\mintty.exe --title "Remote terminal" --size "200`,70" --position "%X%`,%Y%" --class mintty_ssh --exec /bin/ssh -p 65000 rr-@sakuya.pl -t /bin/bash -i, %CygPath%/bin
		return
	}
	^!]::
	{
		X := 45
		Y := 45
		if (!WinExist("ahk_class mintty_ssh"))
		{
			Run, %CygPath%\bin\mintty.exe --title "Remote terminal" --size "200`,70" --position "%X%`,%Y%" --class mintty_ssh --exec /bin/ssh -p 65000 rr-@sakuya.pl, %CygPath%/bin
			WinWait ahk_class mintty_ssh
		}
		else
		{
			WinShow
			WinActivate
		}
		return
	}
	;text editor - activate/run + fix screen position
	+^!N::
	{
		IfWinExist, ahk_class Vim
		{
			WinShow
			WinActivate
		}
		else
		{
			Run, %CygPath%\bin\mintty.exe --size "240`,90" --position "100`,45" --class "Vim" --exec /bin/bash -l -c /bin/vim, %CygPath%/bin
			WinWait, ahk_class Vim
		}
		WinMove, , , 100, 100, 1600, 900
		return
	}
	+PrintScreen::MakeScreen("-g")
	!PrintScreen::MakeScreen("-r active-window --shift -8,-8,+16,+16 --force-gui")
	!#PrintScreen::MakeScreen("-r active-monitor")
	#PrintScreen::MakeScreen("-r all-monitors")

;explorer - activate/run
#E::FRun("ahk_class CabinetWClass", "Z:\", "Z:\")

;foobar - activate/run
#If FileExist("C:\program files (x86)\foobar2000\foobar2000.exe")
	Launch_Media::FRun("ahk_class {97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}", "C:\program files (x86)\foobar2000\foobar2000.exe", "C:\Program Files (x86)\foobar2000")

;wtw
#If FileExist(WTWPath)
	;wtw - activate/run
	+^!M::
		if (!WinExist("^WTW"))
		{
			Run, %WTWPath% -x
		}
		else
		{
			WinShow
			WinActivate
		}
		return

;cdisplayex - activate/run
#If FileExist("C:\Program Files (x86)\CDisplayEx\cdisplayex.exe")
	^!/::FRun("CDisplayEx", "C:\Program Files (x86)\CDisplayEx\cdisplayex.exe", "Z:\")

;browser
#If FileExist(BrowserPath)
	;web browser - activate/run
	^!;::
		if WinExist("Mozilla Firefox")
		{
			WinShow
			WinActivate
			Send, !d
		}
		else if WinExist("ahk_class Chrome_WidgetWin_1")
		{
			WinActivate
			ControlFocus, Chrome_OmniboxView1
			SendInput, {Home}+{End}
		}
		else
		{
			IfExist %BrowserPath%
				Run, %BrowserPath%
		}
		return

;windows powershell / cmd functionalities
#IfWinActive ahk_class ConsoleWindowClass
	^V::SendInput {Raw}%clipboard% ;paste on ctrl+v
	^!PgUp::Send ! el{PgUp}{Enter} ;scroll on ctrl+page up
	^!PgDn::Send ! el{PgDn}{Enter} ;scroll on ctrl+page down
#IfWinActive

;acdsee functionalities
#IfWinActive ahk_class ACDBrowser
	WheelDown::Send, ^{Down}^{Space}
	WheelUp::Send, ^{Up}^{Space}
	^!X::
		SendInput {Left 10}{Shift down}{Right 10}{Shift up}
		return
#IfWinActive
#IfWinActive ACDSee
	WheelDown::
		if (A_TickCount - OldTicks >= 80)
		{
			Send, {WheelDown}
			OldTicks := A_TickCount
		}
		return
	]:: ;send to document folder
	RButton & ~LButton::
		FormatTime, Folder,, 'Z:\'
		Send {LAlt down}c
		Send {LAlt up}
		SendInput %Folder%
		Send {Return}
		return
	[:: ;send to upload folder
	~LButton & RButton::
		Send {LAlt down}c
		Send {LAlt up}
		SendInput Z:\hub\yume.pl
		Send {Return}
		Sleep 100
		return
	XButton1::
		WinGet, Transparency, Transparent, A
		if ! Transparency
			WinSet, Transparent, 255, A
		else if Transparency = 255
			WinSet, Transparent, 0, A
		return
	XButton2:: ;start slideshow
		Send {Pause}
		return
#IfWinActive

;other functionalities

;autohotkey - reload this script
!F11::
{
	Process, Close, Rainmeter.exe
	Run, C:\Program Files \Rainmeter\Rainmeter.exe
	return
}
!F12::Reload

;maximize active window
!M::
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf030 ;WM_SYSCOMMAND, SC_MAXIMIZE
	return

;restore active window
!R::
	ActiveHwnd := WinExist("A")
	PostMessage 0x112, 0xf120 ;WM_SYSCOMMAND, SC_RESTORE
	return

;minimize active window
!W::
	if WinActive("WTW$")
	{
		WinHide
	}
	else
	{
		ActiveHwnd := WinExist("A")
		PostMessage 0x112, 0xF020 ;WM_SYSCOMMAND, SC_MINIMIZE
	}
	return

;always on top
!A::WinSet, AlwaysOnTop, Toggle, A

;transparency
#1::WinSet, Transparent, 0, A
#2::WinSet, Transparent, 64, A
#3::WinSet, Transparent, 255, A
#WheelUp::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency + 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return
#WheelDown::
	WinGet, Transparency, Transparent, A
	if ! Transparency
		Transparency = 255
	Transparency := Transparency - 16
	if Transparency <= 10
	{
		WinSet, Transparent, 10, A
	}
	else if Transparency >= 255
	{
		WinSet, Transparent, 255, A
		WinSet, Transparent, OFF, A
	}
	else
	{
		WinSet, Transparent, %Transparency%, A
	}
	return

;explorer
#IfWinActive ahk_class CabinetWClass
	;new folder
	^n::
		Send {AppsKey}wf
		return
#IfWinActive

;adobe products
#IfWinActive ahk_group adobe
	~LAlt::return
	LAlt UP::Send {Escape}
#IfWinActive