Advertisement
Muche42

TimeClickers.ahk

Oct 22nd, 2015
144
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ;
  2. ; v1.0
  3. ; by Muche, parts by DevourlordGig
  4. ;
  5. #Persistent
  6. #NoEnv
  7. #Warn All
  8. ; Warn,LocalSameAsGlobal used to give way too many false positives
  9. ; I had to encapsulate a couple of subroutines into functions, so their variables could become local and don't clutter global variable space
  10. ;CoordMode, Tooltip, Relative
  11. CoordMode, Pixel, Client
  12. CoordMode, Mouse, Client
  13. SendMode Input
  14. Thread, NoTimers
  15. Thread, Interrupt, 60
  16. winName = Time Clickers ahk_class UnityWndClass
  17.  
  18. IfWinNotExist, %winname%
  19. {
  20.     MsgBox, Window '%winname%' not found`, exiting...
  21.     ExitApp
  22. }
  23.  
  24. showdebug := false ; show debug tooltips
  25. reqActiveWindow := true ; false - try to do stuff in the background, true - activate the window and do stuff in it in the foreground
  26. autoclick := false
  27. StartFromIdxWindowActive := 0 ; it would be better to initialize this near subroutines that use it, but can't, because apparently they are not in the autoexecute section of the script
  28.  
  29. cooldown_time := 50*60*1000 ; cooldown time for cooldown active ability; changing it might need some scheduled actions to be moved around
  30. cooldown_delay := 2*60*1000 ; delay of the first cooldown active ability; changing it might need some scheduled actions to be moved around
  31. DimpsTest := false ; do Dimps' test - don't accumulate active ability time due to killed bosses' max.damage bonus for the first few minutes of the run
  32. DimpsTest_time := 10*60*1000 ; amount of time skipped; changing it might need some scheduled actions to be moved around
  33.  
  34. ;
  35. ;
  36. ; Autoupgrader configuration
  37. ;
  38. ;
  39.  
  40. autoupgrader := new CAutoupgrader
  41. autoupgrader.RegisterCallback("b", "Beeper")
  42. autoupgrader.RegisterCallback("A", "ClickUpgrade_A") ; direct nondelayed team upgrades, needed for priority actions to avoid deadlock
  43. autoupgrader.RegisterCallback("S", "ClickUpgrade_S")
  44. autoupgrader.RegisterCallback("D", "ClickUpgrade_D")
  45. autoupgrader.RegisterCallback("F", "ClickUpgrade_F")
  46. autoupgrader.RegisterCallback("G", "ClickUpgrade_G")
  47. autoupgrader.RegisterCallback(" ", "ClickAbility_space")
  48. autoupgrader.RegisterCallback("7", "ClickAbility_7")
  49. autoupgrader.RegisterCallback("p", "PriorityActions")
  50. autoupgrader.RegisterCallback("Q", "ClickDelayedUpgrade_A") ; delayed team upgrades, for usual cyclic upgrading
  51. autoupgrader.RegisterCallback("W", "ClickDelayedUpgrade_S")
  52. autoupgrader.RegisterCallback("E", "ClickDelayedUpgrade_D")
  53. autoupgrader.RegisterCallback("R", "ClickDelayedUpgrade_F")
  54. autoupgrader.RegisterCallback("T", "ClickDelayedUpgrade_G")
  55.  
  56. autoupgrader.AddAction(1000, " pQ")
  57. autoupgrader.AddActionRelative(1000, " 7")
  58. autoupgrader.AddActionRelative(1000, " pW")
  59. autoupgrader.AddActionRelative(1000, " ")
  60. autoupgrader.AddActionRelative(1000, " pE")
  61. autoupgrader.AddActionRelative(1000, " ")
  62. autoupgrader.AddActionRelative(1000, " pR")
  63. autoupgrader.AddActionRelative(1000, " ")
  64. autoupgrader.AddActionRelative(1000, " pT")
  65. autoupgrader.AddActionRelative(1000, " ")
  66.  
  67. ; autoupgrader's priority colors & actions configuration
  68. ; color scheme used requires Day mode and Post screen effects disabled
  69. ;
  70. ; RGB colors:
  71. ; 0x400000 (darkred)        - SpecOps Training levelup available
  72. ; 0x800000 (darkred-mouseover)
  73. ; 0x000000 (black)      - SpecOps initiation, availability unknown
  74. ;          (black-mouseover=deepred)
  75. ; 0xFF7B00 (orange)     - SpecOps skill available
  76. ; 0xFF9C3E (orange-mouseover)
  77. ; --------
  78. ; 0x29FF00 (lightgreen)     - SpecOps levelup available
  79. ; 0x5EFF3E (lightgreen-mouseover)
  80. ; 0x200000 (deepred)        - SpecOps Training levelup not available
  81. ; 0x401F00 (brown/darkorange)   - SpecOps skill not available
  82. ;          (darkorange-mouseover=darkorange)
  83. ; --------
  84. ; 0x0067FF (lightblue)      - basic upgrade available
  85. ; 0x3E8DFF (lightblue-mouseover)
  86. ; 0x001A40 (darkblue)       - no (basic) upgrade available
  87. ; 0x0A4000 (darkgreen)      - SpecOps levelup not available
  88. ;          (darkgreen-mouseover=darkgreen)
  89.  
  90. ; team upgrade indicators
  91. autoupgrader.RegisterColor(0x400000, true, true, "darkred - SpecOps Training levelup")
  92. autoupgrader.RegisterColor(0x800000, true, true, "darkred-mouseover - SpecOps Training levelup")
  93. autoupgrader.RegisterColor(0x000000, true, true, "black - SpecOps initiation")
  94. autoupgrader.RegisterColor(0xFF7B00, true, false, "orange - SpecOps skill")
  95. autoupgrader.RegisterColor(0xFF9C3E, true, false, "orange-mouseover - SpecOps skill")
  96. autoupgrader.RegisterColor(0x29FF00, false, false, "lightgreen - SpecOps levelup")
  97. autoupgrader.RegisterColor(0x5EFF3E, false, false, "lightgreen-mouseover - SpecOps levelup")
  98. autoupgrader.RegisterColor(0x200000, false, true, "deepred - SpecOps Training levelup")
  99. autoupgrader.RegisterColor(0x401F00, false, false, "darkorange - SpecOps skill")
  100. autoupgrader.RegisterColor(0x0067FF, false, false, "lightblue - basic upgrade")
  101. autoupgrader.RegisterColor(0x3E8DFF, false, false, "lightblue-mouseover - basic upgrade")
  102. autoupgrader.RegisterColor(0x001A40, false, false, "darkblue - no (basic) upgrade")
  103. autoupgrader.RegisterColor(0x0A4000, false, false, "darkgreen - SpecOps levelup")
  104. autoupgrader.RegisterColor(0x294000, false, false, "darkgreen2 - SpecOps levelup")
  105. ; gold OP indicator
  106. autoupgrader.RegisterColor(0xFFE600, true, false, "yellow gold OP")
  107.  
  108. ; TODO: these variables remain to clutter the global variable space, it would be better to enclose them in a function in the future
  109. x1 := 17, x2 := 102, x3 := 125
  110. yd := 96, y1 := 215, y3 := 229
  111. ;autoupgrader.AddPriorityAction(x1, y1, "A", "A_left")
  112. autoupgrader.AddPriorityAction(x2, y1, "A", "A_right")
  113. autoupgrader.AddPriorityAction(x3, y3, "AAA", "A_goldOP")
  114. ;autoupgrader.AddPriorityAction(x1, y1+yd, "S", "S_left")
  115. autoupgrader.AddPriorityAction(x2, y1+yd, "S", "S_right")
  116. autoupgrader.AddPriorityAction(x3, y3+yd, "SSS", "S_goldOP")
  117. ;autoupgrader.AddPriorityAction(x1, y1+2*yd, "D", "D_left")
  118. autoupgrader.AddPriorityAction(x2, y1+2*yd, "D", "D_right")
  119. autoupgrader.AddPriorityAction(x3, y3+2*yd, "DDD", "D_goldOP")
  120. ;autoupgrader.AddPriorityAction(x1, y1+3*yd, "F", "F_left")
  121. autoupgrader.AddPriorityAction(x2, y1+3*yd, "F", "F_right")
  122. autoupgrader.AddPriorityAction(x3, y3+3*yd, "FFF", "F_goldOP")
  123. ;autoupgrader.AddPriorityAction(x1, y1+4*yd, "G", "G_left")
  124. autoupgrader.AddPriorityAction(x2, y1+4*yd, "G", "G_right")
  125. autoupgrader.AddPriorityAction(x3, y3+4*yd, "GGG", "G_goldOP")
  126.  
  127. ;autoupgrader.AddDelayCoord(x1, y1, "A_left")
  128. autoupgrader.AddDelayCoord(x2, y1, "A_right")
  129. ;autoupgrader.AddDelayCoord(x1, y1+yd, "S_left")
  130. autoupgrader.AddDelayCoord(x2, y1+yd, "S_right")
  131. ;autoupgrader.AddDelayCoord(x1, y1+2*yd, "D_left")
  132. autoupgrader.AddDelayCoord(x2, y1+2*yd, "D_right")
  133. ;autoupgrader.AddDelayCoord(x1, y1+3*yd, "F_left")
  134. autoupgrader.AddDelayCoord(x2, y1+3*yd, "F_right")
  135. ;autoupgrader.AddDelayCoord(x1, y1+4*yd, "G_left")
  136. autoupgrader.AddDelayCoord(x2, y1+4*yd, "G_right")
  137.  
  138. ;
  139. ;
  140. ; Scheduler configuration
  141. ;
  142. ;
  143.  
  144. scheduler := new CScheduler
  145. scheduler.RegisterCallback("b", "Beeper")
  146. scheduler.RegisterCallback("A", "ClickUpgrade_A")
  147. scheduler.RegisterCallback("S", "ClickUpgrade_S")
  148. scheduler.RegisterCallback("D", "ClickUpgrade_D")
  149. scheduler.RegisterCallback("F", "ClickUpgrade_F")
  150. scheduler.RegisterCallback("G", "ClickUpgrade_G")
  151. scheduler.RegisterCallback(" ", "ClickAbility_space")
  152. scheduler.RegisterCallback("7", "ClickAbility_7")
  153. scheduler.RegisterCallback("0", "ClickAbility_0")
  154. scheduler.RegisterCallback("m", "ClickBuyMode") ; the game uses Z for buy mode
  155. scheduler.RegisterCallback("c", "EnableAutoclick")
  156. scheduler.RegisterCallback("u", "EnableAutoupgrade")
  157. scheduler.RegisterCallback("W", "Warp")
  158. scheduler.RegisterCallback("R", "RestartScheduler")
  159. scheduler.RegisterCallback("[", "ClickIdleLauncher") ; the game uses Q for click launcher idle mode
  160. scheduler.RegisterCallback("]", "ClickIdleCannon") ; the game uses W for click cannon idle mode
  161. scheduler.RegisterCallback("\", "ClickIdlePistol") ; the game uses E for click pistol idle mode
  162. scheduler.RegisterCallback("p", "PriorityActions")
  163. if DimpsTest {
  164.     scheduler.RegisterCallback("k", "UnlockAbilities")
  165. }
  166.  
  167. scheduler.AddAction(1000, "[]", "disable click launcher & cannon") ; to improve performance during Overpowered waves on my old PC (framerate drop below 30 FPS with them enabled)
  168. if DimpsTest {
  169.     scheduler.AddAction(DimpsTest_time, "k", "Unlock abilities - Dimps' test") ; may have to be moved if the DimpsTest_time is changed
  170. }
  171. scheduler.AddAction(17*60*1000, "[[", "enable click launcher") ; click launcher is OK in this phase, click cannon not, performance-wise
  172. scheduler.AddAction(20*60*1000, "b")
  173. scheduler.AddAction(21*60*1000, "bb")
  174. scheduler.AddAction((21*60+40)*1000, "FGb" , "2000|")
  175.  
  176. scheduler.AddActionRelative(61*1000, "AA", "2100|")
  177. scheduler.AddActionRelative(63*1000, "SS", "2100|")
  178. scheduler.AddActionRelative(59*1000, "DDb", "2100|")
  179. scheduler.AddActionRelative(57*1000, "FF", "2100|")
  180. scheduler.AddActionRelative(58*1000, "GG", "2100|")
  181.  
  182. scheduler.AddActionRelative(55*1000, "AA", "2200|")
  183. scheduler.AddActionRelative(55*1000, "SS", "2200|")
  184. scheduler.AddActionRelative(55*1000, "DDb", "2200|")
  185. scheduler.AddActionRelative(53*1000, "FF", "2200|")
  186. scheduler.AddActionRelative(53*1000, "GG", "2200|")
  187.  
  188. scheduler.AddActionRelative(54*1000, "AA", "2300|")
  189. scheduler.AddActionRelative(55*1000, "SS", "2300|")
  190. scheduler.AddActionRelative(56*1000, "DDb", "2300|")
  191. scheduler.AddActionRelative(55*1000, "FF", "2300|")
  192. scheduler.AddActionRelative(55*1000, "GG", "2300|")
  193.  
  194. scheduler.AddActionRelative(54*1000, "AA", "2400|")
  195. scheduler.AddActionRelative(55*1000, "SS", "2400|")
  196. scheduler.AddActionRelative(56*1000, "DDb", "2400|")
  197. scheduler.AddActionRelative(56*1000, "FF", "2400|")
  198. scheduler.AddActionRelative(53*1000, "GG", "2400|")
  199.  
  200. scheduler.AddActionRelative(54*1000, "AA", "2500|")
  201. scheduler.AddActionRelative(55*1000, "SS", "2500|")
  202. scheduler.AddActionRelative(55*1000, "DDb", "2500|")
  203. scheduler.AddActionRelative(54*1000, "FF", "2500|")
  204. scheduler.AddActionRelative(54*1000, "GGbb", "2500|")
  205.  
  206. scheduler.AddActionRelative((6*60+13)*1000, "AAAAAAb", "|2700+")
  207. if !DimpsTest {
  208.     timediff := scheduler.AddAction(cooldown_delay + cooldown_time, "0", "#1")
  209. } else {
  210.     timediff := 0
  211. }
  212. scheduler.AddActionRelative(93*1000 - timediff, "SSSSSSb", "|2700+")
  213. scheduler.AddActionRelative(83*1000, "DDDDDDb", "|2700+")
  214. scheduler.AddActionRelative(78*1000, "FFFFFFb", "|2700+")
  215. scheduler.AddActionRelative(72*1000, "GGGGGGAb", "|2700+")
  216.  
  217. scheduler.AddActionRelative(68*1000, "ppAAASb", "|2800+")
  218. scheduler.AddActionRelative(67*1000, "ppSSSDb", "|2800+")
  219. scheduler.AddActionRelative(65*1000, "ppDDDFb", "|2800+")
  220. if DimpsTest {
  221.     timediff := scheduler.AddAction(DimpsTest_time + cooldown_time, "0", "#1")
  222. } else {
  223.     timediff := 0
  224. }
  225. scheduler.AddActionRelative(64*1000 - timediff, "ppFFFGb", "|2800+")
  226. scheduler.AddActionRelative(65*1000, "ppGGGAb", "|2800+")
  227.  
  228. scheduler.AddActionRelative(63*1000, "ppAAASb", "|2900+")
  229. scheduler.AddActionRelative(70*1000, "ppSSSDb", "|2900+")
  230. scheduler.AddActionRelative(64*1000, "pDDDFb", "|2900+")
  231. scheduler.AddActionRelative(60*1000, "FFFGb", "|2900+")
  232. scheduler.AddActionRelative(58*1000, "GGGbb", "|2900+")
  233.  
  234. scheduler.AddActionRelative(1000, "ASDFG")
  235. scheduler.AddActionRelative(1000, "mmm", "buy mode from Promotion to Upgrade")
  236. scheduler.AddActionRelative(1000, "cu", "enable autoclick and autoupgrade")
  237. scheduler.AddActionRelative(1000, "bbbb")
  238.  
  239. if DimpsTest {
  240.     scheduler.AddAction(DimpsTest_time + 2*cooldown_time, "0", "#2")
  241. } else {
  242.     scheduler.AddAction(cooldown_delay + 2*cooldown_time, "0", "#2")
  243. }
  244. scheduler.AddActionRelative(cooldown_time, "0", "#3")
  245. timediff := scheduler.AddAction((2*3600+40*60)*1000, "]]", "enable click cannon")
  246. if !DimpsTest {
  247.     scheduler.AddActionRelative(cooldown_time - timediff, "0", "#4")
  248. }
  249.  
  250. scheduler.AddAction((4*3600+45*60+10*60)*1000, "bb 07 ", "last prewarp abilities usage")
  251. ; +10min due to halloween bonuses
  252. ; could be another +5min till max.damage time ends, but last 5min is crawl anyway so keeping it short for higher efficiency
  253. scheduler.AddActionRelative(3*60*1000, "bbbbbWbR", "Warp, restart scheduler")
  254.  
  255. ;
  256. ;
  257. ; Utilities
  258. ;
  259. ;
  260.  
  261. ; returns formatted time (_h__m__s)
  262. ; @param time integer  time to be formatted (in milliseconds)
  263. ; @return string
  264. FormatTime(time) {
  265.     if time is not integer
  266.     {
  267.         MsgBox, % "[" . A_ThisFunc . "(" . time . ")] time (" . time . ") is not an integer"
  268.         return
  269.     }
  270.     if (time < 0) {
  271.         time := Abs(time)
  272.         sign := -1
  273.     } else {
  274.         sign := 1
  275.     }
  276.  
  277.     h := time // 3600000
  278.     m := mod(time, 3600000) // 60000
  279.     s := mod(time, 60000) // 1000
  280.     ms := mod(time, 1000)
  281.  
  282.     res := (sign < 0) ? "-" : ""
  283.     if (h > 0) {
  284.         res .= h . "h"
  285.     }
  286.     if (m < 10 && h > 0) {
  287.         res .= "0" . m . "m"
  288.     } else if (m > 0) {
  289.         res .= m . "m"
  290.     }
  291.     if (s < 10 && (h > 0 || m > 0)) {
  292.         res .= "0" . s . "s"
  293.     } else {
  294.         res .= s . "s"
  295.     }
  296. /*  ; is it useful to have milliseconds shown?
  297.     if (ms > 0) {
  298.         if (ms >= 100) {
  299.             res .= ms . "ms"
  300.         } else if (ms >= 10) {
  301.             res .= "0" . ms . "ms"
  302.         } else {
  303.             res .= "00" . ms . "ms"
  304.         }
  305.     }
  306. */
  307.  
  308.     return res
  309. }
  310.  
  311. tooltipoff:
  312. ToolTip
  313. return
  314.  
  315. tooltipDoff:
  316. ToolTip, , , ,2
  317. return
  318.  
  319. Beeper() {
  320.     SoundBeep
  321. }
  322.  
  323. ;
  324. ;
  325. ; Scheduler & Autoupgrader classes
  326. ;
  327. ;
  328.  
  329. class CScheduler
  330. {
  331.     regCallbacks := {} ; registered callbacks for actions; associative array, item is (key: function)
  332.     actions := [] ; array of all timed actions; item is {time":time, "keys":keys, "comment":comment}
  333.     started := false ; true if it's been started, remains true even if all actions were performed
  334.     nextActionIdx := 0 ; index of next timed action to be performed when it's time
  335.     timeStart := 0
  336.     timeShift := 0 ; time shift, to allow interactive alteration of the timeline, in milliseconds
  337.  
  338.     ; registers callback for given key to be used in actions
  339.     ; @param key  1-character string, not case sentitive
  340.     ; @param funcname  function name
  341.     RegisterCallback(key, funcname) {
  342.         if (StrLen(key) != 1) {
  343.             MsgBox, % "[" . A_ThisFunc . "(" . key . "," . funcname . ")] key's length is " . StrLen(key) . ", expected 1"
  344.             return
  345.         }
  346.         param := IsFunc(funcname)
  347.         if (!param) {
  348.             MsgBox, % "[" . A_ThisFunc . "(" . key . "," . funcname . ")] parameter '" . funcname . "' is not a function"
  349.             return
  350.         }
  351.         if (param > 1) {
  352.             MsgBox, % "[" . A_ThisFunc . "(" . key . "," . funcname . ")] function '" . funcname . "' requires " . (param-1) . " parameter(s), expected 0"
  353.             return
  354.         }
  355.         if (this.regCallbacks[key]) {
  356.             MsgBox, % "[" . A_ThisFunc . "(" . key . "," . funcname . ")] callback for key '" . key . "' already registered to '" . (this.regCallbacks[key]) . "'"
  357.             return
  358.         }
  359.         this.regCallbacks[key] := funcname
  360.     }
  361.  
  362.     ; checks if each key in keys has a registered callback
  363.     ; @return true/false
  364.     CheckKeys(keys) {
  365.         Loop, % StrLen(keys) {
  366.             key := SubStr(keys, A_Index, 1)
  367.             if (!this.regCallbacks.HasKey(key)) {
  368.                 return false
  369.             }
  370.         }
  371.         return true
  372.     }
  373.  
  374.     ; adds new action at the end of the array of actions
  375.     ; @param time integer  time at which (or later) this action should be executed, in milliseconds
  376.     ; @param keys string  sequence of letters, each of which describes what will be executed via its callback
  377.     ; @param comment string
  378.     ; @return integer  time difference from previous action, 0 if there is no previous action, in milliseconds
  379.     AddAction(time, keys, comment := "") {
  380.         if time is not integer
  381.         {
  382.             MsgBox, % "[" . A_ThisFunc . "(" . time . "," . keys . "," . comment . ")] time (" . time . ") is not an integer"
  383.             return
  384.         }
  385.         if (time < 0) {
  386.             MsgBox, % "[" . A_ThisFunc . "(" . time . "," . keys . "," . comment . ")] time (" . time . ") is negative"
  387.             return
  388.         }
  389.         if (!this.CheckKeys(keys)) {
  390.             MsgBox, % "[" . A_ThisFunc . "(" . time . "," . keys . "," . comment . ")] some of keys '" . keys . "' have no registered callback"
  391.             return
  392.         }
  393.         if (this.actions.Length() > 0) {
  394.             lastAction := this.actions[this.actions.Length()]
  395.             if (time < lastAction.time) {
  396.                 MsgBox, % "[" . A_ThisFunc . "(" . time . "," . keys . "," . comment . ")] time (" . time . " - " . FormatTime(time) . ") is earlier than last action's time (" . lastAction.time . " - " . FormatTime(lastAction.time) . ")"
  397.             } else {
  398.                 this.actions.Push({"time": time, "keys": keys, "comment": comment})
  399.                 return (time - lastAction.time)
  400.             }
  401.         } else {
  402.             this.actions.Push({"time": time, "keys": keys, "comment": comment})
  403.             return 0
  404.         }
  405.     }
  406.  
  407.     ; adds new action at the end, time is relative to the last action's time
  408.     ; @param timerel integer  time difference from the last action at which (or later) this action should be executed, in milliseconds
  409.     ; @param keys string  sequence of letters, each of which describes what will be executed via its callback
  410.     ; @param comment string
  411.     AddActionRelative(timerel, keys, comment := "") {
  412.         if timerel is not integer
  413.         {
  414.             MsgBox, % "[" . A_ThisFunc . "(" . timerel . "," . keys . "," . comment . ")] timerel (" . timerel . ") is not an integer"
  415.             return
  416.         }
  417.         if (timerel < 0) {
  418.             MsgBox, % "[" . A_ThisFunc . "(" . timerel . "," . keys . "," . comment . ")] timerel (" . timerel . ") is negative"
  419.             return
  420.         }
  421.         if (this.actions.Length() <= 0) {
  422.             MsgBox, % "[" . A_ThisFunc . "(" . timerel . "," . keys . "," . comment . ")] last action not found"
  423.             return
  424.         }
  425.         if (!this.CheckKeys(keys)) {
  426.             MsgBox, % "[" . A_ThisFunc . "(" . timerel . "," . keys . "," . comment . ")] some of keys '" . keys . "' have no registered callback"
  427.             return
  428.         }
  429.         lastAction := this.actions[this.actions.Length()]
  430.         newtime := lastAction.time + timerel
  431.         this.actions.Push({"time": newtime, "keys": keys, "comment": comment})
  432.     }
  433.  
  434.     ; start timer
  435.     Start() {
  436.         this.nextActionIdx := 1
  437.         this.timeStart := A_TickCount
  438.         this.timeShift := 0
  439.         this.started := true
  440.     }
  441.  
  442.     ; starts timer with alternative first action
  443.     ; @param idx integer  index of the first executed action
  444.     StartFromIndex(idx) {
  445.         if idx is not integer
  446.         {
  447.             MsgBox, % "[" . A_ThisFunc . "(" . idx . ")] idx (" . idx . ") is not an integer"
  448.             return
  449.         }
  450.         if (idx < 1) {
  451.             MsgBox, % "[" . A_ThisFunc . "(" . idx . ")] idx (" . idx . ") is less than 1"
  452.             return
  453.         }
  454.         if (idx > this.actions.Length()) {
  455.             MsgBox, % "[" . A_ThisFunc . "(" . idx . ")] idx (" . idx . ") is greater than number of actions (" . this.actions.Length() . ")"
  456.             return
  457.         }
  458.  
  459.         this.nextActionIdx := idx
  460.         this.timeStart := A_TickCount
  461.         this.timeShift := -this.actions[idx].time
  462.         this.started := true
  463.     }
  464.  
  465.     Stop() {
  466.         this.nextActionIdx := 0
  467.         this.timeStart := 0
  468.         this.timeShift := 0
  469.         this.started := false
  470.     }
  471.  
  472.     ; executes the next action if it's time for it
  473.     ; @return boolean  true if there are more actions waiting, false otherwise
  474.     Next() {
  475.         elapsedTime := A_TickCount - this.timeStart - this.timeShift
  476.         nextAction := this.actions[this.nextActionIdx]
  477.         if nextAction {
  478.             if (elapsedTime >= nextAction.time) {
  479.                 this.nextActionIdx++ ; increase index first, so restarting this scheduler within callback is correct
  480.                 ; execute scheduled action
  481.                 keys := nextAction.keys
  482.                 old_IsCritical := A_IsCritical
  483.                 Critical
  484.                 Loop, % StrLen(keys) {
  485.                     key := SubStr(keys, A_Index, 1)
  486.                     funcname := this.regCallbacks[key]
  487.                     %funcname%()
  488.                 }
  489.                 Critical, %old_IsCritical%
  490.             }
  491.         }
  492.         return (this.nextActionIdx <= this.actions.Length())
  493.     }
  494.  
  495.     ; returns next scheduled action, with time property updated to reflect passed time since start
  496.     ; @return  false if no next action exists, {"time":time, "keys":keys, "comment":comment} otherwise
  497.     GetNextActionRelative() {
  498.         nextAction := this.actions[this.nextActionIdx]
  499.         if nextAction {
  500.             newTime := nextAction.time - (A_TickCount - this.timeStart - this.timeShift)
  501.             return {"time": newTime, "keys": nextAction.keys, "comment": nextAction.comment}
  502.         } else {
  503.             return false
  504.         }
  505.     }
  506.  
  507.     ; returns time elapsed since the start (timeshift not taken into account)
  508.     ; @return integer
  509.     GetElapsedTime() {
  510.         return A_TickCount - this.timeStart
  511.     }
  512.  
  513.     ; shifts perceived time so next scheduled action is executed earlier/later
  514.     ; @param time integer  shift next actions' time earlier (negative number) / later (positive number)
  515.     ShiftTime(time) {
  516.         if time is not integer
  517.         {
  518.             MsgBox, % "[" . A_ThisFunc . "(" . time . ")] time (" . time . ") is not an integer"
  519.             return
  520.         }
  521.         this.timeShift += time
  522.     }
  523.  
  524.     ; returns timeshift
  525.     ; @return integer
  526.     GetShiftTime() {
  527.         return this.timeShift
  528.     }
  529.  
  530.     ; returns basic description
  531.     ; @return string
  532.     GetDescription() {
  533.         n := this.actions.Length()
  534.         return "Number of scheduled actions: " . n . ", last action at " . FormatTime(this.actions[n].time)
  535.     }
  536.  
  537.     ; returns time till last scheduled action
  538.     ; @return  integer, or empty if scheduled hasn't been started yet or there are no actions scheduled
  539.     GetRemainingTime() {
  540.         n := this.actions.Length()
  541.         lastAction := this.actions[n]
  542.         if (!lastAction || !this.started) {
  543.             return
  544.         }
  545.         return (lastAction.time - (A_TickCount - this.timeStart - this.timeShift))
  546.     }
  547.  
  548.     ; adds scheduled actions into specified listbox
  549.     ; @param listbox string  name of the listbox variable
  550.     AddListBoxItems(listbox) {
  551.         Loop, % this.actions.Length() {
  552.             action := this.actions[A_Index]
  553.             s := A_index . ". " . FormatTime(action.time) . " (" . action.keys . ")" . (action.comment ? " - " . action.comment : "")
  554.             GuiControl, , %listbox%, %s%
  555.         }
  556.  
  557.         ; preselect active pending action if applicable
  558.         if (this.started && this.nextActionIdx <= this.actions.Length()) {
  559.             GuiControl, Choose, %listbox%, % this.nextActionIdx
  560.         }
  561.     }
  562. }
  563.  
  564. class CAutoupgrader extends CScheduler
  565. {
  566.     colors := {} ; associative array of colors approved for priority & delay upgrade, item is (color: {"priority":boolean, "delay":boolean, "comment":comment})
  567.     priorityActions := [] ; array of all priority actions, item is {"coords":{"x":x, "y":y}, "keys":keys, "comment":comment}
  568.     delayCoords := [] ; array of all delaying coordinates, item is {"coords":{"x":x, "y":y}, "comment":comment}
  569.  
  570.     ; executes the next action if it's time for it
  571.     ; restarts scheduled actions if there are no more actions
  572.     ; @return boolean  true if there are more actions waiting, false otherwise
  573.     ; @Override
  574.     Next() {
  575.         elapsedTime := A_TickCount - this.timeStart - this.timeShift
  576.         nextAction := this.actions[this.nextActionIdx]
  577.         if nextAction {
  578.             if (elapsedTime >= nextAction.time) {
  579.                 this.nextActionIdx++ ; increase index first, so restarting this scheduler within callback is correct
  580.                 ; execute scheduled action
  581.                 keys := nextAction.keys
  582.                 old_IsCritical := A_IsCritical
  583.                 Critical
  584.                 Loop, % StrLen(keys) {
  585.                     key := SubStr(keys, A_Index, 1)
  586.                     funcname := this.regCallbacks[key]
  587.                     %funcname%()
  588.                 }
  589.                 Critical, %old_IsCritical%
  590.  
  591.                 if (this.nextActionIdx > this.actions.Length()) {
  592.                     this.Start()
  593.                 }
  594.             }
  595.         }
  596.         return (this.nextActionIdx <= this.actions.Length())
  597.     }
  598.  
  599.     ; registers color to be used in priority actions & delaying coordinates
  600.     ; @param color integer  colorID, usually specified in 0xRRGGBB format
  601.     ; @param priority boolean  true - color is used for priority actions, false - color is used for debugging/displaying purposes only
  602.     ; @param delay boolean  true - color is used for delaying coordinates, false - color is used for debugging/displaying purposes only
  603.     ; @param comment string
  604.     RegisterColor(color, priority, delay, comment := "") {
  605.         if color is not integer
  606.         {
  607.             MsgBox, % "[" . A_ThisFunc . "(" . color . "," . priority . "," . delay . "," . comment . ")] color (" . color . ") is not an integer"
  608.             return
  609.         }
  610.         if (this.colors[color]) {
  611.             otherColor := this.colors[color]
  612.             MsgBox, % "[" . A_ThisFunc . "(" . color . "," . priority . "," . delay . "," . comment . ")] color (" . color . ") has already been registered to (" . otherColor.priority . "," . otherColor.delay . "," . otherColor.comment . ")"
  613.             return
  614.         }
  615.         this.colors[color] := {"priority": priority, "delay": delay, "comment": comment}
  616.     }
  617.  
  618.     ; adds priority action
  619.     ; @param x integer  x-coordinate of the pixel to be checked
  620.     ; @param y integer  y-coortinate of the pixel to be checked
  621.     ; @param keys string  sequence of letters, each of which describes what will be executed via its callback
  622.     ; @param comment string
  623.     AddPriorityAction(x, y, keys, comment := "") {
  624.         if x is not integer
  625.         {
  626.             MsgBox, % "[" . A_ThisFunc . "(" . x . "," . y . "," . keys . "," . comment . ")] x (" . x . ") is not an integer"
  627.             return
  628.         }
  629.         if y is not integer
  630.         {
  631.             MsgBox, % "[" . A_ThisFunc . "(" . x . "," . y . "," . keys . "," . comment . ")] y (" . y . ") is not an integer"
  632.             return
  633.         }
  634.         if (!this.CheckKeys(keys)) {
  635.             MsgBox, % "[" . A_ThisFunc . "(" . x . "," . y . "," . keys . "," . comment . ")] some of keys '" . keys . "' have no registered callback"
  636.             return
  637.         }
  638.         this.priorityActions.Push({"coords":{"x":x, "y":y}, "keys":keys, "comment":comment})
  639.     }
  640.  
  641.     ; collects information about priority actions & current colors they reference
  642.     ; assumes the game window is active, otherwise pixel color detection won't work
  643.     ; @return string
  644.     GetPriorityActionsStatus() {
  645.         s := ""
  646.         Loop, % this.priorityActions.Length() {
  647.             action := this.priorityActions[A_Index]
  648.             PixelGetColor, pixelColor, action.coords.x, action.coords.y, RGB
  649.             color := this.colors[pixelColor]
  650.             s .= (StrLen(s) > 0) ? "`n" : ""
  651.             s .= "[" . action.coords.x . "," . action.coords.y . "] (" . action.keys . ")" . (action.comment ? " - " . action.comment : "") . ", color=" . pixelColor
  652.             if color {
  653.                 s .= ", priority=" . (color.priority ? "true" : "false") . ", delay=" . (color.delay ? "true" : "false") . (color.comment ? ", comment=" . color.comment : "")
  654.             } else {
  655.                 s .= " (color not recognized)"
  656.             }
  657.         }
  658.  
  659.         return s
  660.     }
  661.  
  662.     ; checks priority actions and executes them if applicable
  663.     ; assumes the game window is active, otherwise pixel color detection won't work
  664.     ; @return string  info about executed actions, empty string if none were executed
  665.     CheckPriorityActions() {
  666.         s := ""
  667.         Loop, % this.priorityActions.Length() {
  668.             action := this.priorityActions[A_Index]
  669.             PixelGetColor, pixelColor, action.coords.x, action.coords.y, RGB
  670.             color := this.colors[pixelColor]
  671.             if (color && color.priority) {
  672.                 ; priority action is activated, execute it
  673.                 keys := action.keys
  674.                 s .= (StrLen(s) > 0) ? "`n" : ""
  675.                 s .= "[" . action.coords.x . "," . action.coords.y . "] (" . keys . ")" . (action.comment ? " - " . action.comment : "") . ", color=" . pixelColor
  676.                 s .= (color.comment ? ", colorComment=" . color.comment : "")
  677.                 old_IsCritical := A_IsCritical
  678.                 Critical
  679.                 Loop, % StrLen(keys) {
  680.                     key := SubStr(keys, A_Index, 1)
  681.                     funcname := this.regCallbacks[key]
  682.                     %funcname%()
  683.                 }
  684.                 Critical, %old_IsCritical%
  685.             }
  686.         }
  687.  
  688.         return s
  689.     }
  690.  
  691.     ; adds delaying coordinates
  692.     ; @param x integer  x-coordinate of the pixel to be checked
  693.     ; @param y integer  y-coortinate of the pixel to be checked
  694.     ; @param comment string
  695.     AddDelayCoord(x, y, comment := "") {
  696.         if x is not integer
  697.         {
  698.             MsgBox, % "[" . A_ThisFunc . "(" . x . "," . y . "," . comment . ")] x (" . x . ") is not an integer"
  699.             return
  700.         }
  701.         if y is not integer
  702.         {
  703.             MsgBox, % "[" . A_ThisFunc . "(" . x . "," . y . "," . comment . ")] y (" . y . ") is not an integer"
  704.             return
  705.         }
  706.         this.delayCoords.Push({"coords":{"x":x, "y":y}, "comment":comment})
  707.     }
  708.  
  709.     ; collects information about delaying coordinates & current colors they reference
  710.     ; @return string
  711.     GetDelayStatus() {
  712.         s := ""
  713.         Loop, % this.delayCoords.Length() {
  714.             coord := this.delayCoords[A_Index]
  715.             PixelGetColor, pixelColor, coord.coords.x, coord.coords.y, RGB
  716.             color := this.colors[pixelColor]
  717.             s .= (StrLen(s) > 0) ? "`n" : ""
  718.             s .= "[" . coord.coords.x . "," . coord.coords.y . "]" . (coord.comment ? " - " . coord.comment : "") . ", color=" . pixelColor
  719.             if color {
  720.                 s .= ", priority=" . (color.priority ? "true" : "false") . ", delay=" . (color.delay ? "true" : "false") . (color.comment ? ", comment=" . color.comment : "")
  721.             } else {
  722.                 s .= " (color not recognized)"
  723.             }
  724.         }
  725.  
  726.         return s
  727.     }
  728.  
  729.     ; checks priority actions and executes them if applicable
  730.     ; assumes the game window is active
  731.     ; @return boolean  true if delay is active, false otherwise
  732.     CheckDelayCoords() {
  733.         Loop, % this.delayCoords.Length() {
  734.             coord := this.delayCoords[A_Index]
  735.             PixelGetColor, pixelColor, coord.coords.x, coord.coords.y, RGB
  736.             color := this.colors[pixelColor]
  737.             if (color && color.delay) {
  738.                 ; delay is activated
  739.                 return true
  740.             }
  741.         }
  742.  
  743.         return false
  744.     }
  745. }
  746.  
  747. ;
  748. ;
  749. ; Game interaction / Callback functions
  750. ;
  751. ;
  752.  
  753. ; upgrade given team member
  754. ; @param key string  key to send to upgrade the member
  755. ; @param posx integer  x-position of the member's button
  756. ; @param posy integer  y-position of the member's button
  757. ClickUpgrade(key, posx, posy) {
  758.     global winName, reqActiveWindow
  759.  
  760.     if reqActiveWindow {
  761.         currentWinID := 0
  762.         IfWinNotActive, %winName%
  763.         {
  764.             currentWinID := WinActive("A")
  765.             WinActivate, %winname%
  766.         }
  767.         Send, %key%
  768.         Sleep, 200 ; increased delay to improve reliability - very important for scheduler, not so much for autoupgrader
  769.         if (currentWinID != 0) {
  770.             WinActivate, ahk_id %currentWinID%
  771.         }
  772.     } else {
  773.         ; ControlSend seems to send keys into the buffer, buffer gets executed only after the window gets activated, which we don't want to in this case
  774.         ; ControlClick seems to click only to window, not the specified position
  775. ;       ControlClick, % "x" . posx . " y" . posy, %winName%
  776.         ControlClick2(posx, posy, winName)
  777.         Sleep, 200 ; increased delay to improve reliability - very important for scheduler, not so much for autoupgrader
  778.     }
  779. }
  780.  
  781. ClickUpgrade_A() {
  782.     ClickUpgrade("a", 70, 245)
  783. }
  784.  
  785. ClickUpgrade_S() {
  786.     ClickUpgrade("s", 70, 340)
  787. }
  788.  
  789. ClickUpgrade_D() {
  790.     ClickUpgrade("d", 70, 445)
  791. }
  792.  
  793. ClickUpgrade_F() {
  794.     ClickUpgrade("f", 70, 545)
  795. }
  796.  
  797. ClickUpgrade_G() {
  798.     ClickUpgrade("g", 70, 645)
  799. }
  800.  
  801. ClickAbility_space() {
  802.     ClickUpgrade("{Space}", 1200, 525)
  803. }
  804.  
  805. ClickAbility_7() {
  806.     ClickUpgrade("7", 1075, 475)
  807. }
  808.  
  809. ClickAbility_0() {
  810.     ClickUpgrade("0", 1245, 475)
  811. }
  812.  
  813. ; change idle mode of click launcher
  814. ClickIdleLauncher() {
  815.     ClickUpgrade("q", 385, 700)
  816. }
  817.  
  818. ; change idle mode of click cannon
  819. ClickIdleCannon() {
  820.     ClickUpgrade("w", 580, 700)
  821. }
  822.  
  823. ; change idle mode of click pistol
  824. ClickIdlePistol() {
  825.     ClickUpgrade("e", 890, 700)
  826. }
  827.  
  828. ; checks autoupgrader's delaying coordinates if allowed
  829. ; @return boolean  true if the delay is activated, false otherwise
  830. CheckDelay() {
  831.     global winName, reqActiveWindow, autoupgrader
  832.  
  833.     if !reqActiveWindow {
  834.         return false
  835.     }
  836.  
  837.     currentWinID := 0
  838.     IfWinNotActive, %winName%
  839.     {
  840.         currentWinID := WinActive("A")
  841.         WinActivate, %winname%
  842.     }
  843.     b := autoupgrader.CheckDelayCoords()
  844.     if (currentWinID != 0) {
  845.         WinActivate, ahk_id %currentWinID%
  846.     }
  847.  
  848.     return b
  849. }
  850.  
  851. ClickDelayedUpgrade_A() {
  852.     if !CheckDelay() {
  853. ;       ToolTip, Team upgrade A without delay, 50, 0, 2
  854. ;       SetTimer, tooltipdoff, -2000
  855.         ClickUpgrade_A()
  856.     } else {
  857. ;       ToolTip, Team upgrade A delayed, 50, 0, 2
  858. ;       SetTimer, tooltipdoff, -2000
  859.     }
  860. }
  861.  
  862. ClickDelayedUpgrade_S() {
  863.     if !CheckDelay() {
  864. ;       ToolTip, Team upgrade S without delay, 50, 0, 2
  865. ;       SetTimer, tooltipdoff, -2000
  866.         ClickUpgrade_S()
  867.     } else {
  868. ;       ToolTip, Team upgrade S delayed, 50, 0, 2
  869. ;       SetTimer, tooltipdoff, -2000
  870.     }
  871. }
  872.  
  873. ClickDelayedUpgrade_D() {
  874.     if !CheckDelay() {
  875. ;       ToolTip, Team upgrade D without delay, 50, 0, 2
  876. ;       SetTimer, tooltipdoff, -2000
  877.         ClickUpgrade_D()
  878.     } else {
  879. ;       ToolTip, Team upgrade D delayed, 50, 0, 2
  880. ;       SetTimer, tooltipdoff, -2000
  881.     }
  882. }
  883.  
  884. ClickDelayedUpgrade_F() {
  885.     if !CheckDelay() {
  886. ;       ToolTip, Team upgrade F without delay, 50, 0, 2
  887. ;       SetTimer, tooltipdoff, -2000
  888.         ClickUpgrade_F()
  889.     } else {
  890. ;       ToolTip, Team upgrade F delayed, 50, 0, 2
  891. ;       SetTimer, tooltipdoff, -2000
  892.     }
  893. }
  894.  
  895. ClickDelayedUpgrade_G() {
  896.     if !CheckDelay() {
  897. ;       ToolTip, Team upgrade G without delay, 50, 0, 2
  898. ;       SetTimer, tooltipdoff, -2000
  899.         ClickUpgrade_G()
  900.     } else {
  901. ;       ToolTip, Team upgrade G delayed, 50, 0, 2
  902. ;       SetTimer, tooltipdoff, -2000
  903.     }
  904. }
  905.  
  906.  
  907. ; click buy mode (upgrade selector)
  908. ; @param count integer  number of times it should be clicked
  909. ClickBuyMode(count := 1) {
  910.     global winName, reqActiveWindow
  911.  
  912.     if (count < 1) {
  913.         ; display just tooltip, not MsgBox, in case it's part of scheduler, and we don't want to stop it
  914.         ToolTip, % "[" . A_ThisFunc . "(" . count . ")] count (" . count . ") is less than 1. WHY?", 50, 50
  915.         SetTimer, tooltipoff, -2000
  916.         return
  917.     }
  918.     if reqActiveWindow {
  919.         currentWinID := 0
  920.         IfWinNotActive, %winName%
  921.         {
  922.             currentWinID := WinActive("A")
  923.             WinActivate, %winname%
  924.         }
  925.         Loop, %count% {
  926.             Send, Z
  927.             Sleep, 200 ; increased delay to improve reliability - very important for scheduler, not so much for autoupgrader
  928.         }
  929.         if (currentWinID != 0) {
  930.             WinActivate, ahk_id %currentWinID%
  931.         }
  932.     } else {
  933.         ; ControlSend seems to send keys into the buffer, buffer gets executed only after the window gets activated, which we don't want to in this case
  934.         ; ControlClick seems to click only to window, not the specified position
  935. ;       ControlClick, % "x" . posx . " y" . posy, %winName%
  936.         Loop, %count% {
  937.             ControlClick2(35, 700, winName)
  938.             Sleep, 200 ; increased delay to improve reliability - very important for scheduler, not so much for autoupgrader
  939.         }
  940.     }
  941. }
  942.  
  943. ; needed for Dimps' test
  944. UnlockAbilities() {
  945.     global winName
  946.  
  947.     ; abilities unlocking
  948.     ToolTip, [UnlockAbilities] Unlocking abilities..., 50, 50
  949.     SetTimer, tooltipoff, -2500
  950.     Loop, 10 {
  951.         ClickUpgrade("c", 1220, 330)
  952.     }
  953.  
  954.     ; use abilities - potentially increase max.damage time; they will have enough time for cooldown anyways
  955.     ToolTip, [UnlockAbilities] Using abilities..., 50, 50
  956.     SetTimer, tooltipoff, -2000
  957.     ClickAbility_7()
  958.     ClickAbility_space()
  959.     ClickAbility_0()
  960. }
  961.  
  962. PriorityActions() {
  963.     global winName, reqActiveWindow, showdebug, autoupgrader
  964.  
  965.     if reqActiveWindow {
  966.         ; priority actions require active window
  967.         currentWinID := 0
  968.         IfWinNotActive, %winName%
  969.         {
  970.             currentWinID := WinActive("A")
  971.             WinActivate, %winname%
  972.         }
  973.         s := autoupgrader.CheckPriorityActions()
  974.         if (showdebug && StrLen(s)>0) {
  975.             ToolTip, % "Priority actions executed:`n" . s, 50, 0, 2
  976.             SetTimer, tooltipDoff, -10000
  977.         }
  978.         if (currentWinID != 0) {
  979.             WinActivate, ahk_id %currentWinID%
  980.         }
  981.     }
  982. }
  983.  
  984. ;
  985. ;
  986. ; Warp
  987. ;
  988. ;
  989.  
  990. ; asks user for confirmation to do warp
  991. ; @return boolean  true if user confirmed, false otherwise
  992. AskWarp() {
  993.     MsgBox, % 1+32, , Make sure the Buy mode is on Upgrade`, otherwise team upgrades will be incorrect after the warp.`nContinue?, 10
  994.     IfMsgBox, OK
  995.     {
  996.         return true
  997.     } else IfMsgBox, Cancel
  998.     {
  999.         return false
  1000.     } else IfMsgBox, Timeout
  1001.     {
  1002.         return false
  1003.     } else {
  1004.         return false
  1005.     }
  1006. }
  1007.  
  1008. ; Assumes the buy mode is on Upgrade!
  1009. ; @param interactive boolean  function is interactive, i.e. is allowed to ask the user questions and such
  1010. Warp(interactive := false) {
  1011.     global winName, reqActiveWindow, DimpsTest
  1012.  
  1013.     ; disable autoclick and autoupgrade
  1014.     ToolTip, [Warp] Disabling autoclick and autoupgrade..., 50, 50
  1015.     SetTimer, tooltipoff, -2000
  1016.     SetAutoclick(false, false)
  1017.     SetAutoupgrade(false, false)
  1018.     Sleep, 2000
  1019.  
  1020.     old_IsCritical := A_IsCritical
  1021.     Critical
  1022.  
  1023.     ; activate the window (overriding reqActiveWindow setting)
  1024.     ToolTip, [Warp] Activating the window..., 50, 50
  1025.     SetTimer, tooltipoff, -2000
  1026.     currentWinID := 0
  1027.     IfWinNotActive, %winName%
  1028.     {
  1029.         currentWinID := WinActive("A")
  1030.         WinActivate, %winname%
  1031.     }
  1032.     oldReqActiveWindow := reqActiveWindow
  1033.     reqActiveWindow := true
  1034.  
  1035.     ; ask for confirmation if allowed
  1036.     ; TODO: check Buy mode automatically and warn the user if incorrect
  1037.     if (interactive && !AskWarp()) {
  1038.         ; warp cancelled
  1039.         reqActiveWindow := oldReqActiveWindow
  1040.         if (currentWinID != 0) {
  1041.             WinActivate, ahk_id %currentWinID%
  1042.         }
  1043.         Critical, %old_IsCritical%
  1044.         ToolTip, [Warp] Warp cancelled., 50, 50
  1045.         SetTimer, tooltipoff, -5000
  1046.         return
  1047.     }
  1048.  
  1049.     ; warp
  1050.     ToolTip, [Warp] Warping..., 50, 50
  1051.     SetTimer, tooltipoff, -5500
  1052.     ControlClick2(1220, 330, winName)
  1053.     Sleep, 500
  1054.     ControlClick2(540, 530, winName) ; yes
  1055.     Sleep, 5000
  1056.  
  1057.     ; click pistol upgrades
  1058.     ToolTip, [Warp] Upgrading click pistol..., 50, 50
  1059.     SetTimer, tooltipoff, -3500
  1060.     Loop, 13 {
  1061.         ClickUpgrade("H", 1220, 240)
  1062.     }
  1063.  
  1064.     if !DimpsTest {
  1065.         ; abilities unlocking
  1066.         ToolTip, [Warp] Unlocking abilities..., 50, 50
  1067.         SetTimer, tooltipoff, -2500
  1068.         Loop, 10 {
  1069.             ClickUpgrade("C", 1220, 330)
  1070.         }
  1071.  
  1072.         ; use abilities - increase max.damage time; they will have enough time for cooldown anyways
  1073.         ; team upgrades trigger overpowered mode very quickly, so have to use abilities before them to gain any benefit
  1074.         ToolTip, [Warp] Use initial abilities..., 50, 50
  1075.         SetTimer, tooltipoff, -2000
  1076.         ClickAbility_7()
  1077.         ClickAbility_space()
  1078.         ClickAbility_0()
  1079.     }
  1080.  
  1081.     ; team upgrades
  1082.     ToolTip, [Warp] Upgrading team (buy mode: Upgrade->Max)..., 50, 50
  1083.     SetTimer, tooltipoff, -2000
  1084.     ClickBuyMode(2)
  1085.     ToolTip, [Warp] Upgrading team (phase 1: Max)..., 50, 50
  1086.     SetTimer, tooltipoff, -3000
  1087.     Loop, 3 { ; Level 2000|
  1088.         ClickUpgrade_A()
  1089.         ClickUpgrade_S()
  1090.         ClickUpgrade_D()
  1091.     }
  1092.     Loop, 2 { ; Level |1000
  1093.         ClickUpgrade_F()
  1094.         ClickUpgrade_G()
  1095.     }
  1096.     ToolTip, [Warp] Upgrading team (buy mode: Max->Promotion)..., 50, 50
  1097.     SetTimer, tooltipoff, -2000
  1098.     ClickBuyMode(3)
  1099.     ToolTip, [Warp] Upgrading team (phase 2: Promotion)..., 50, 50
  1100.     SetTimer, tooltipoff, -8000
  1101.     Loop, 19 { ; Level |1900+
  1102.         ClickUpgrade_F()
  1103.         ClickUpgrade_G()
  1104.     }
  1105.  
  1106.     ; idle mode: rocket launcher & click pistol
  1107.     ToolTip, [Warp] Enabling idle mode for rocket launcher & click pistol..., 50, 50
  1108.     SetTimer, tooltipoff, -2000
  1109.     ControlClick2(800, 300, winName) ; click in the middle of the window, so the Particle Ball tooltip disappears - isn't needed anymore in v1.3 due to hotkeys
  1110.     Sleep, 200
  1111.     ClickIdleLauncher() ; idle mode: rocket launcher
  1112.     ClickIdleCannon() ; idle mode: click cannon
  1113.     ClickIdlePistol() ; idle mode: click pistol
  1114.  
  1115.     reqActiveWindow := oldReqActiveWindow
  1116.     if (currentWinID != 0) {
  1117.         WinActivate, ahk_id %currentWinID%
  1118.     }
  1119.     Critical, %old_IsCritical%
  1120.     ToolTip, [Warp] Warp finished., 50, 50
  1121.     SetTimer, tooltipoff, -5000
  1122. }
  1123.  
  1124. ;
  1125. ;
  1126. ; Autoclick control
  1127. ;
  1128. ;
  1129.  
  1130. ; enable/disable autoclick
  1131. ; @param status boolean  new status
  1132. ; @param display boolean  display tooltip with new status
  1133. SetAutoclick(status, display := true) {
  1134.     global autoclick
  1135.  
  1136.     if status {
  1137.         autoclick := true
  1138.         SetTimer, autoclicker, 30
  1139.         if display {
  1140.             ToolTip, Autoclick enabled, 50, 50
  1141.             SetTimer, tooltipoff, -2000
  1142.         }
  1143.     } else {
  1144.         autoclick := false
  1145.         SetTimer, autoclicker, Off
  1146.         if display {
  1147.             ToolTip, Autoclick disabled, 50, 50
  1148.             SetTimer, tooltipoff, -2000
  1149.         }
  1150.     }
  1151. }
  1152.  
  1153. EnableAutoclick() {
  1154.     SetAutoclick(true, false)
  1155. }
  1156.  
  1157. ; toggle autoclick
  1158. #c::    ; Win+C
  1159. SetAutoclick(!autoclick, true)
  1160. return
  1161.  
  1162. autoclicker:
  1163. ControlClick2(800, 300, winName)
  1164. return
  1165.  
  1166. ;
  1167. ;
  1168. ; Autoupgrade control
  1169. ;
  1170. ;
  1171.  
  1172. ; enable/disable autoupgrader
  1173. ; @param status boolean  new status
  1174. ; @param display boolean  display tooltip with new status
  1175. SetAutoupgrade(status, display := true) {
  1176.     global autoupgrader
  1177.  
  1178.     if status {
  1179.         autoupgrader.Start()
  1180.         SetTimer, autoupgraderTimer, 1000
  1181.         if display {
  1182.             ToolTip, Autoupgrader enabled, 50, 50
  1183.             SetTimer, tooltipoff, -2000
  1184.         }
  1185.     } else {
  1186.         autoupgrader.Stop()
  1187.         SetTimer, autoupgraderTimer, Off
  1188.         if display {
  1189.             ToolTip, Autoupgrader disabled, 50, 50
  1190.             SetTimer, tooltipoff, -2000
  1191.         }
  1192.     }
  1193. }
  1194.  
  1195. EnableAutoupgrade() {
  1196.     SetAutoupgrade(true, false)
  1197. }
  1198.  
  1199. ;toggle autoupgrader
  1200. #m::    ; Win+M
  1201. SetAutoupgrade(!autoupgrader.started, true)
  1202. return
  1203.  
  1204. AutoupgraderTimer() {
  1205.     global showdebug, autoupgrader
  1206.  
  1207.     n := autoupgrader.Next()
  1208.     if (false && showdebug) {
  1209.         nextAction := autoupgrader.GetNextActionRelative()
  1210.         if nextAction {
  1211.             t := "Next action in " . FormatTime(nextAction.time) . " (" . nextAction.keys . ")" . (nextAction.comment ? " - " . nextAction.comment : "")
  1212.         } else {
  1213.             t := "No next action found"
  1214.         }
  1215.         ToolTip, % "[AutoupgraderTimer] " . t, 50, 0, 2
  1216.         SetTimer, tooltipDoff, -1250 ; higher than timer's 1000ms to avoid flickering
  1217.     }
  1218.     if (!n) {
  1219.         ; all scheduled actions finished, why didn't autoupgrader autorestart? Are there any actions scheduled at all?
  1220.         MsgBox, [AutoupgraderTimer] No next action found`, are there any actions scheduled at all?
  1221.     }
  1222. }
  1223.  
  1224. autoupgraderTimer:
  1225. AutoupgraderTimer()
  1226. return
  1227.  
  1228. ;
  1229. ;
  1230. ; Scheduler control
  1231. ;
  1232. ;
  1233.  
  1234. RestartScheduler() {
  1235.     global scheduler
  1236.  
  1237.     SetAutoupgrade(false, false)
  1238.     scheduler.Start()
  1239.     SetTimer, schedulerTimer, 1000
  1240.     ToolTip, % "Scheduler restarted - " . scheduler.GetDescription(), 50, 50
  1241.     SetTimer, tooltipoff, % -10000
  1242. }
  1243.  
  1244. ; start scheduler
  1245. ^!s::   ; Ctrl+Alt+S
  1246. SetAutoupgrade(false, false)
  1247. scheduler.Start()
  1248. SetTimer, schedulerTimer, 1000
  1249. ToolTip, % "Scheduler started - " . scheduler.GetDescription(), 50, 50
  1250. SetTimer, tooltipoff, % -10000
  1251. return
  1252.  
  1253. SchedulerTimer() {
  1254.     global showdebug, scheduler
  1255.  
  1256.     n := scheduler.Next()
  1257.     if (showdebug) {
  1258.         nextAction := scheduler.GetNextActionRelative()
  1259.         if nextAction {
  1260.             t := "Next action in " . FormatTime(nextAction.time) . " (" . nextAction.keys . ")" . (nextAction.comment ? " - " . nextAction.comment : "")
  1261.         } else {
  1262.             t := "No next action found"
  1263.         }
  1264.         ToolTip, % "[SchedulerTimer] " . t, 50, 0, 2
  1265.         SetTimer, tooltipDoff, -1250 ; higher than timer's 1000ms to avoid flickering
  1266.     }
  1267.     if (!n) {
  1268.         ; all scheduled actions finished
  1269.         SetTimer, schedulerTimer, Off
  1270.         ToolTip, Scheduler finished`, enabling autoupgrade and autoclick, 50, 50
  1271.         SetTimer, tooltipoff, -5000
  1272.         ; enable autoclick
  1273.         SetAutoclick(true, false)
  1274.         ; enable autoupgrader
  1275.         SetAutoupgrade(true, false)
  1276.     }
  1277. }
  1278.  
  1279. schedulerTimer:
  1280. SchedulerTimer()
  1281. return
  1282.  
  1283. ; shift scheduler time, add time for subsequent action
  1284. #NumpadAdd::    ; Win+NumpadPlus
  1285. scheduler.ShiftTime(5*1000) ; 5s
  1286. ToolTip, % "Scheduler's time shifted +5s, total of " . FormatTime(scheduler.GetShiftTime()), 50, 50
  1287. SetTimer, tooltipoff, -2000
  1288. return
  1289.  
  1290. ; shift scheduler time, subtract time for subsequent action
  1291. #NumpadSub::    ; Win+NumpadMinus
  1292. scheduler.ShiftTime(-5*1000) ; 5s
  1293. ToolTip, % "Scheduler's time shifted -5s, total of " . FormatTime(scheduler.GetShiftTime()), 50, 50
  1294. SetTimer, tooltipoff, -2000
  1295. return
  1296.  
  1297. ; shift scheduler time, add more time for subsequent action
  1298. #+NumpadAdd::   ; Win+Shift+NumpadPlus
  1299. scheduler.ShiftTime(60*1000) ; 60s
  1300. ToolTip, % "Scheduler's time shifted +60s, total of " . FormatTime(scheduler.GetShiftTime()), 50, 50
  1301. SetTimer, tooltipoff, -2000
  1302. return
  1303.  
  1304. ; shift scheduler time, subtract more time for subsequent action
  1305. #+NumpadSub::   ; Win+Shift+NumpadMinus
  1306. scheduler.ShiftTime(-60*1000) ; 60s
  1307. ToolTip, % "Scheduler's time shifted -60s, total of " . FormatTime(scheduler.GetShiftTime()), 50, 50
  1308. SetTimer, tooltipoff, -2000
  1309. return
  1310.  
  1311. ;
  1312. ;
  1313. ; Start scheduler from index stuff
  1314. ;
  1315. ;
  1316.  
  1317. #NumpadMult::   ; Win+NumpadAsterisk
  1318. if (StartFromIdxWindowActive) {
  1319.     ; window is already active, wait till it's closed
  1320.     ToolTip, Start scheduler from index window is already active, 50, 50
  1321.     SetTimer, tooltipoff, -2000
  1322. } else {
  1323.     StartFromIdxWindowActive := 1
  1324.     Gui, Add, Text,, Select first action
  1325.     Gui, Add, ListBox, AltSubmit vFirstActionListBox R30 W400 gListBoxClicked,
  1326.     Gui, Add, Button, Default, OK
  1327.     Gui, +Delimiter`n
  1328.     ;GuiControl, -Redraw, FirstActionListBox
  1329.     scheduler.AddListBoxItems("FirstActionListBox")
  1330.     ;GuiControl, +Redraw, FirstActionListBox
  1331.     Gui, Show
  1332. }
  1333. return
  1334.  
  1335. GuiClose:
  1336. GuiEscape:
  1337. Gui, Cancel
  1338. Gui, Destroy
  1339. StartFromIdxWindowActive := 0
  1340. return
  1341.  
  1342. ButtonOK:
  1343. Gui, Submit
  1344. Gui, Destroy
  1345. StartFromIdxWindowActive := 0
  1346. SetAutoupgrade(false, false)
  1347. scheduler.StartFromIndex(FirstActionListBox)
  1348. SetTimer, schedulerTimer, 1000
  1349. ToolTip, % "Scheduler started at index " . FirstActionListBox . " - " . scheduler.GetDescription(), 50, 50
  1350. SetTimer, tooltipoff, % -10000
  1351. return
  1352.  
  1353. ListBoxClicked:
  1354. if (A_GuiEvent = "DoubleClick") {
  1355. ;   ToolTip, double click, 50, 50
  1356. ;   SetTimer, tooltipoff, -2000
  1357.     GoSub, ButtonOK
  1358.     ; doubleclick seems to get through to the underlying window after the list window closes, didn't investigate further
  1359. ;} else {
  1360. ;   ToolTip, normal click, 50, 50
  1361. ;   SetTimer, tooltipoff, -2000
  1362. }
  1363. return
  1364.  
  1365. ;
  1366. ;
  1367. ; Other control
  1368. ;
  1369. ;
  1370.  
  1371. #r::Reload  ; Win+R
  1372.  
  1373. ^!p::Pause  ; Ctrl+Alt+P
  1374.  
  1375. #w::    ; Win+W
  1376. WinMinimize, %winName%
  1377. return
  1378.  
  1379. #q::    ; Win+Q
  1380. WinActivate, %winName%
  1381. return
  1382.  
  1383. #d::    ; Win+D
  1384. showdebug := !showdebug
  1385. ToolTip, % "Debug info " . (showdebug ? "enabled" : "disabled"), 50, 50
  1386. SetTimer, tooltipoff, -2000
  1387. return
  1388.  
  1389. #a::    ; Win+A
  1390. reqActiveWindow := !reqActiveWindow
  1391. ToolTip, % "Active window switching " . (reqActiveWindow ? "enabled" : "disabled"), 50, 50
  1392. SetTimer, tooltipoff, -2000
  1393. return
  1394.  
  1395.  
  1396. ; @return string
  1397. GetStatusInfo() {
  1398.     global autoclick, reqActiveWindow, showdebug, autoupgrader, scheduler
  1399.  
  1400.     s := "Autoclick is " . (autoclick ? "enabled" : "disabled")
  1401.     s .= "`nActive window switching is " . (reqActiveWindow ? "enabled" : "disabled")
  1402.     s .= "`nDebug info is " . (showdebug ? "enabled" : "disabled")
  1403.     if autoupgrader.started {
  1404.         s .= "`nAutoupgrader started " . FormatTime(autoupgrader.GetElapsedTime()) . " ago"
  1405.         remTime := autoupgrader.GetRemainingTime()
  1406.         if (remTime >= 0) {
  1407.             s .= ", wraps around in " . FormatTime(remTime)
  1408.         } else {
  1409.             s .= ", ended " . FormatTime(Abs(remTime)) . " ago" ; happens when time for the last scheduled action already passed, but action is yet to be executed
  1410.         }
  1411.         if (autoupgrader.getShiftTime() != 0) {
  1412.             s .= "`nAutoupgrader's time shift is " . FormatTime(autoupgrader.GetShiftTime())
  1413.         }
  1414.         nextAction := autoupgrader.GetNextActionRelative()
  1415.         if nextAction {
  1416.             s .= "`nNext scheduled autoupgrade action in " . FormatTime(nextAction.time) . " (" . nextAction.keys . ")" . (nextAction.comment ? " - " . nextAction.comment : "")
  1417.         } else {
  1418.             s .= "`nNo next scheduled autoupgrade action found"
  1419.         }
  1420.     } else {
  1421.         s .= "`nAutoupgrader is disabled"
  1422.     }
  1423.     if scheduler.started {
  1424.         s .= "`nScheduler started " . FormatTime(scheduler.GetElapsedTime()) . " ago"
  1425.         remTime := scheduler.GetRemainingTime()
  1426.         if (remTime >= 0) {
  1427.             s .= ", ends in " . FormatTime(remTime)
  1428.         } else {
  1429.             s .= ", ended " . FormatTime(Abs(remTime)) . " ago"
  1430.         }
  1431.         if (scheduler.getShiftTime() != 0) {
  1432.             s .= "`nScheduler's time shift is " . FormatTime(scheduler.GetShiftTime())
  1433.         }
  1434.         nextAction := scheduler.GetNextActionRelative()
  1435.         if nextAction {
  1436.             s .= "`nNext scheduled action in " . FormatTime(nextAction.time) . " (" . nextAction.keys . ")" . (nextAction.comment ? " - " . nextAction.comment : "")
  1437.         } else {
  1438.             s .= "`nNo next scheduled action found"
  1439.         }
  1440.     }
  1441.  
  1442.     return s
  1443. }
  1444.  
  1445. ; show status info
  1446. #s::    ; Win+S
  1447. ToolTip, % GetStatusInfo(), 50, 50
  1448. SetTimer, tooltipoff, -5000
  1449. return
  1450.  
  1451. #z::    ; Win+Z
  1452. ; name of this (global) variable is _s, not s, so functions using s don't complain due to Warn,LocalSameAsGlobal
  1453. _s := "Priority actions & colors:`n"
  1454. _s .= autoupgrader.GetPriorityActionsStatus()
  1455. _s .= "`nDelay coordinates & colors:`n"
  1456. _s .= autoupgrader.GetDelayStatus()
  1457. ToolTip, %_s%, 50, 0, 2
  1458. SetTimer, tooltipDoff, -15000
  1459. ; copy information into Windows clipboard, so the colors could be pasted and used for configuration
  1460. clipboard := _s
  1461. return
  1462.  
  1463. #u::    ; Win+U
  1464. ClickBuyMode()
  1465. return
  1466.  
  1467. ; Warp
  1468. ^!w::   ; Ctrl+Alt+W
  1469. Warp(true)
  1470. return
  1471.  
  1472. ;
  1473. ;
  1474. ; Click into non-active window
  1475. ; by DevourlordGig
  1476. ; see http://pastebin.com/raw.php?i=JNthRMMf
  1477. ; see https://www.reddit.com/r/TimeClickers/comments/3hhfvm/my_tc_script/cumtv85
  1478. ;
  1479. ;
  1480.  
  1481. ; Retrieves the control at the specified point.
  1482. ; X [in] X-coordinate relative to the top-left of the window.
  1483. ; Y [in] Y-coordinate relative to the top-left of the window.
  1484. ; WinTitle [in] Title of the window whose controls will be searched.
  1485. ; WinText [in]
  1486. ; cX [out] X-coordinate relative to the top-left of the control.
  1487. ; cY [out] Y-coordinate relative to the top-left of the control.
  1488. ; ExcludeTitle [in]
  1489. ; ExcludeText [in]
  1490. ; Return Value: The hwnd of the control if found, otherwise the hwnd of the window.
  1491. ControlFromPoint(X, Y, WinTitle="", WinText="", ByRef cX="", ByRef cY="", ExcludeTitle="", ExcludeText="")
  1492. {
  1493.     static EnumChildFindPointProc = 0
  1494.     if !EnumChildFindPointProc
  1495.         EnumChildFindPointProc := RegisterCallback("EnumChildFindPoint", "Fast")
  1496.  
  1497.     if !(target_window := WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText))
  1498.         return false
  1499.  
  1500.     VarSetCapacity(rect, 16)
  1501.     DllCall("GetWindowRect", "uint", target_window, "uint", &rect)
  1502.     VarSetCapacity(pah, 36, 0)
  1503.     NumPut(X + NumGet(rect,0,"int"), pah, 0, "int")
  1504.     NumPut(Y + NumGet(rect,4,"int"), pah, 4, "int")
  1505.     DllCall("EnumChildWindows", "uint", target_window, "uint", EnumChildFindPointProc, "uint", &pah)
  1506.     control_window := NumGet(pah,24) ? NumGet(pah,24) : target_window
  1507.     DllCall("ScreenToClient", "uint", control_window, "uint", &pah)
  1508.     cX := NumGet(pah,0,"int"), cY := NumGet(pah,4,"int")
  1509.     return control_window
  1510. }
  1511.  
  1512. ; Ported from AutoHotkey::script2.cpp::EnumChildFindPoint()
  1513. EnumChildFindPoint(aWnd, lParam) {
  1514.     if !DllCall("IsWindowVisible", "uint", aWnd)
  1515.         return true
  1516.  
  1517.     VarSetCapacity(rect, 16)
  1518.     if !DllCall("GetWindowRect", "uint", aWnd, "uint", &rect)
  1519.         return true
  1520.  
  1521.     pt_x := NumGet(lParam+0,0,"int"), pt_y := NumGet(lParam+0,4,"int")
  1522.     rect_left := NumGet(rect,0,"int"), rect_right := NumGet(rect,8,"int")
  1523.     rect_top := NumGet(rect,4,"int"), rect_bottom := NumGet(rect,12,"int")
  1524.  
  1525.     if (pt_x >= rect_left && pt_x <= rect_right && pt_y >= rect_top && pt_y <= rect_bottom) {
  1526.         center_x := rect_left + (rect_right - rect_left) / 2
  1527.         center_y := rect_top + (rect_bottom - rect_top) / 2
  1528.         distance := Sqrt((pt_x-center_x)**2 + (pt_y-center_y)**2)
  1529.         update_it := !NumGet(lParam+24)
  1530.  
  1531.         if (!update_it) {
  1532.             rect_found_left := NumGet(lParam+8,0,"int"), rect_found_right := NumGet(lParam+8,8,"int")
  1533.             rect_found_top := NumGet(lParam+8,4,"int"), rect_found_bottom := NumGet(lParam+8,12,"int")
  1534.             if (rect_left >= rect_found_left && rect_right <= rect_found_right
  1535.               && rect_top >= rect_found_top && rect_bottom <= rect_found_bottom)
  1536.                 update_it := true
  1537.             else if (distance < NumGet(lParam+28,0,"double")
  1538.               && (rect_found_left < rect_left || rect_found_right > rect_right
  1539.               | rect_found_top < rect_top || rect_found_bottom > rect_bottom))
  1540.                 update_it := true
  1541.         }
  1542.  
  1543.         if (update_it) {
  1544.             NumPut(aWnd, lParam+24)
  1545.             DllCall("RtlMoveMemory","uint",lParam+8,"uint",&rect,"uint",16)
  1546.             NumPut(distance, lParam+28, 0, "double")
  1547.         }
  1548.     }
  1549.  
  1550.     return true
  1551. }
  1552.  
  1553. ; X and Y use CoordMode,Mouse,Window not CoordMode,Mouse,Client
  1554. ControlClick2(X, Y, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="") {
  1555.     global showdebug
  1556.  
  1557.     hwnd := ControlFromPoint(X, Y, WinTitle, WinText, cX, cY, ExcludeTitle, ExcludeText)
  1558.     PostMessage, 0x200, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_MOUSEMOVE
  1559.     if (showdebug && ErrorLevel) {
  1560.         ToolTip, [ControlClick2]#1 ErrorLevel=%ErrorLevel%, 50, 0, 2
  1561.         SetTimer, tooltipDoff, -5000
  1562.         Sleep, 500
  1563.     }
  1564.     PostMessage, 0x2A1, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_MOUSEHOVER
  1565.     if (showdebug && ErrorLevel) {
  1566.         ToolTip, [ControlClick2]#2 ErrorLevel=%ErrorLevel%, 50, 0, 2
  1567.         SetTimer, tooltipDoff, -5000
  1568.         Sleep, 500
  1569.     }
  1570.     PostMessage, 0x201, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_LBUTTONDOWN
  1571.     if (showdebug && ErrorLevel) {
  1572.         ToolTip, [ControlClick2]#3 ErrorLevel=%ErrorLevel%, 50, 0, 2
  1573.         SetTimer, tooltipDoff, -5000
  1574.         Sleep, 500
  1575.     }
  1576.     PostMessage, 0x202, 0, cX&0xFFFF | cY<<16,, ahk_id %hwnd% ; WM_LBUTTONUP
  1577.     if (showdebug && ErrorLevel) {
  1578.         ToolTip, [ControlClick2]#4 ErrorLevel=%ErrorLevel%, 50, 0, 2
  1579.         SetTimer, tooltipDoff, -5000
  1580.         Sleep, 500
  1581.     }
  1582. }
  1583.  
  1584. ;
  1585. ;
  1586. ;
  1587. ;
  1588. ;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement