AutoIt help with while loop bot function

08/11/2017 16:12 anno116611#1
Hi, i am creating a bot and i get stucked in one point of developing a bot.

Its my main loop function where i need to display some stuff on listview but i have problem because all is in while loop so it repeating few times always.

So what i need ?

I need to bot doing next in main loop:
- send tab to select mob ( add "searching mob" to list 1 time untill mob is found )
- if mob is selected and mob hp is greater than 0 send 3 to attack ( add "attacking mob" to list 1 time only untill attacking mob )
- when mob is dead add count +1 ( add "mob killed +1" to list 1 time when mob is dead )
repeat

At the moment it repeat and counting too many mobs, like i killed 2 he count 5 and work like :

searching mob
attacking mob
attacking mob
attacking mob
attacking mob
attacking mob
attacking mob
attacking mob
mobs killed 1

Code:
Func _start()

	$run = Not $run

	If $run = True Then
		GUICtrlSetData($bot_status, "Bot running")
		_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Bot started", 0)
	Else
		GUICtrlSetData($bot_status, "Bot paused")
		_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Bot paused", 0)
	EndIf

	While $run = True
		; get mob hp
		$handles = _KDMemory_OpenProcess($processId)
		$baseAddress = _KDMemory_GetModuleBaseAddress($handles, $moduleName) + $baseOffset
		$memoryData = _KDMemory_ReadProcessMemory($handles, $baseAddress, "DWORD", $enemyHpOffset)

		Local $pickPause = 300
		
		#cs
		
		send tab to select mob												( add "searching mob" to list 1 time untill mob is found )
		if mob is selected and mob hp is greater than 0 send 3 to attack 	( add "attacking mob" to list 1 time only untill attacking mob )
		when mob is dead add count +1										( add "mob killed +1" to list 1 time when mob is dead )
		repeat																( repeat process )
		
		#ce
		
		; if mob hp is grater than zero send 3 to attack it
		If $memoryData[1] > 0 Then
			_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Attacking mob", 0)
			ControlSend($title, "", "", "{3}")
			Sleep(500)
		Else
			; send space after killing mob to pick items
			_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Picking items", 0)
			ControlSend($title, "", "", "{SPACE}")
			Sleep($pickPause)
			ControlSend($title, "", "", "{SPACE}")
			Sleep($pickPause)
			ControlSend($title, "", "", "{SPACE}")
			Sleep($pickPause)
			ControlSend($title, "", "", "{SPACE}")
			Sleep($pickPause)
			ControlSend($title, "", "", "{SPACE}")
			Sleep($pickPause)
			ControlSend($title, "", "", "{SPACE}")
			Sleep($pickPause)
			; send tab to select mob
			_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Searching mob", 0)
			ControlSend($title, "", "", "{TAB}")
			; add +1 to mobs killied
			$mobsKilled += 1
			GUICtrlSetData($killedMobs, $mobsKilled)
			_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Mobs killed - " & $mobsKilled, 0)

		EndIf
		
		; mob hp display
		GUICtrlSetData($enemyHp, $memoryData[1])

		_KDMemory_CloseHandles($handles)

	WEnd

EndFunc
08/12/2017 10:28 °Incinerate#2
This happens in a view milliseconds in case the mobs HP is < 0:
read memory > HP-If-Statement > MobCounter =+1
I think your script is faster than the Game. The mob isnt alreads deleted in game memory and u read the informations again.
Try to Sleep a view seconds before you searching or the next mob.


Btw.
I dont know if it is good or bad, but u set $pickPause always again. Maybe its better to put it above your While-Loop.
And you have 2 statements about $run, maybe you can connect them to 1 statement.
08/12/2017 13:03 anno116611#3
Founded solution, i splited all in functions and now works better and accurate.
Added $pickTimes in global var

And now adding 1 time per action to list:

add "searching mob" 1 time -> searching mob ( sending TAB ) untill mob is founded
add "attacking mob" 1 time -> sending ( 3 ) untill mob hp is 0
add "picking items" 1 time -> pick drop -> send space few times
add mob kill +1
repeating !

Code:
; MAIN LOOP
Func _start()

	; paused global
	$run = Not $run

	; add to list when bot is started/paused
	If $run = True Then
		_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Bot started", 0)
	Else
		_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Bot paused", 0)
	EndIf

	; MAIN LOOP
	; while not paused
	While $run = True

		_findMob()
		_attackMob()
		_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Picking items", 0)
		_pickItems()
	WEnd

EndFunc

; get mob hp, return HP value
Func _getMobHP()
	$handles = _KDMemory_OpenProcess($processId)
	$baseAddress = _KDMemory_GetModuleBaseAddress($handles, $moduleName) + $baseOffset
	$memoryData = _KDMemory_ReadProcessMemory($handles, $baseAddress, "DWORD", $enemyHpOffset)
	Return $memoryData[1]
	_KDMemory_CloseHandles($handles)
EndFunc

; set mob hp to gui
Func _mobHpToGUI()

	If _getMobHP() > 0 Then
		GUICtrlSetData($enemyHp, _getMobHP())
	Else
		GUICtrlSetData($enemyHp, "0")
	EndIf

	Sleep(500)

EndFunc

; find mob
Func _findMob()
	; add to list
	_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Searching mob", 0)
	; send tab untill mob founded, mob hp grater than 0
	Do
		ControlSend($title, "", "", "{TAB}")
		Sleep(500)
	Until _getMobHP() > 0
	; add to list
	_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Attacking mob", 0)
EndFunc

; kill mob
Func _attackMob()
	; send key 3 untill mob is dead
	Do
		ControlSend($title, "", "", "{3}")
		Sleep(500)
	Until _getMobHP() = 0 ; mob dead = 0
	Sleep(100)
	; add count +1
	$mobsKilled += 1
	Sleep(500)
	; set data to mobs killed counter
	GUICtrlSetData($killedMobs, $mobsKilled)
	; add to list
	_GUICtrlListBox_InsertString($List1,  _NowTime() & " : Mobs killed - " & $mobsKilled, 0)
EndFunc


Func _myManaHp()

	Local $handles = _KDMemory_OpenProcess($processId)

	Local $hpAddress = _KDMemory_GetModuleBaseAddress($handles, $moduleName) + $currentHpBaseOffset
	Local $hpFullAddress = _KDMemory_GetModuleBaseAddress($handles, $moduleName) + $baseOffset
	Local $manaAddress = _KDMemory_GetModuleBaseAddress($handles, $moduleName) + $baseOffset
	Local $manaFullAddress = _KDMemory_GetModuleBaseAddress($handles, $moduleName) + $baseOffset

	Local $hpMemoryData = _KDMemory_ReadProcessMemory($handles, $hpAddress, "DWORD", $myHpOffset)
	Local $hpFulllMemoryData = _KDMemory_ReadProcessMemory($handles, $hpFullAddress, "DWORD", $myFullHpOffset)
	Local $manaMemoryData = _KDMemory_ReadProcessMemory($handles, $manaAddress, "DWORD", $myManaOffset)
	Local $manaFullMemoryData = _KDMemory_ReadProcessMemory($handles, $manaFullAddress, "DWORD", $myFullManaOffset)

	GUICtrlSetData($hp, $hpMemoryData[1] & " / " & $hpFulllMemoryData[1])
	Sleep(100)
	GUICtrlSetData($mana, $manaMemoryData[1] & " / " & $manaFullMemoryData[1])
	Sleep(100)

	_KDMemory_CloseHandles($handles)
	Sleep(500)

EndFunc

; picking items, send space with pause between picks
Func _pickItems()
	ControlSend($title, "", "", "{SPACE}")
	Sleep($pickPause)
	ControlSend($title, "", "", "{SPACE}")
	Sleep($pickPause)
	ControlSend($title, "", "", "{SPACE}")
	Sleep($pickPause)
	ControlSend($title, "", "", "{SPACE}")
	Sleep($pickPause)
	ControlSend($title, "", "", "{SPACE}")
	Sleep($pickPause)
	ControlSend($title, "", "", "{SPACE}")
	Sleep($pickPause)
EndFunc

Func _end()
	Exit
EndFunc
08/12/2017 16:35 warfley#4
Just as an advice, you could realize this in your program using a state machine. If you define a set of states your program can currently be in, and a set of viable transition. As your states have each only one successor it's a pretty trivial task:
Code:
Local $Transitions[4][3] = [["SearchState", "AttackState", "StartAttack"],["AttackState", "MobKilledState", "MobKilled"],["MobKilledState", "SearchState", "StartSearch"], ["InitState", "SearchState", "StartSearch"]]

func StartAttack()
   ; attacking code
   ; if worked corectly:
   Return True
   ; otherwise return false
EndFunc

func MobKilled()
   ; mob got killed code
   ; if worked corectly:
   Return True
   ; otherwise return false
EndFunc

func StartSearch()
   ; Searching code
   ; if worked corectly:
   Return True
   ; otherwise return false
EndFunc


func nextState(ByRef $CurrentState)
   For $i = 0 to UBound($Transitions, 1) - 1
	  If ($Transitions[$i][0] = $CurrentState) And (Call($Transitions[$i][2]) = True) Then
		 $CurrentState = $Transitions[$i][1]
		 Return
	  EndIf
   Next
EndFunc

; Main Program:

$currState = "InitState"

While True
   nextState($currState)
WEnd
If its more ambiguous, and a state could have multiple successors, you need to specify them, and use somekind of this function to transition:
Code:
func TransitState(ByRef $CurrentState, Const $NextState)
   For $i = 0 to UBound($Transitions, 1) - 1
	  If ($Transitions[$i][0] = $CurrentState) And ($Transitions[$i][1] = $NextState) And (Call($Transitions[$i][2]) = True) Then
		 $CurrentState = $NextState
		 Return
	  EndIf
   Next
EndFunc
State machines are easy to maintain and to extend, as also produce a very easy to understand code
08/12/2017 17:05 anno116611#5
Quote:
Originally Posted by warfley View Post
Just as an advice, you could realize this in your program using a state machine. If you define a set of states your program can currently be in, and a set of viable transition. As your states have each only one successor it's a pretty trivial task:
Code:
Local $Transitions[4][3] = [["SearchState", "AttackState", "StartAttack"],["AttackState", "MobKilledState", "MobKilled"],["MobKilledState", "SearchState", "StartSearch"], ["InitState", "SearchState", "StartSearch"]]

func StartAttack()
   ; attacking code
   ; if worked corectly:
   Return True
   ; otherwise return false
EndFunc

func MobKilled()
   ; mob got killed code
   ; if worked corectly:
   Return True
   ; otherwise return false
EndFunc

func StartSearch()
   ; Searching code
   ; if worked corectly:
   Return True
   ; otherwise return false
EndFunc


func nextState(ByRef $CurrentState)
   For $i = 0 to UBound($Transitions, 1) - 1
	  If ($Transitions[$i][0] = $CurrentState) And (Call($Transitions[$i][2]) = True) Then
		 $CurrentState = $Transitions[$i][1]
		 Return
	  EndIf
   Next
EndFunc

; Main Program:

$currState = "InitState"

While True
   nextState($currState)
WEnd
If its more ambiguous, and a state could have multiple successors, you need to specify them, and use somekind of this function to transition:
Code:
func TransitState(ByRef $CurrentState, Const $NextState)
   For $i = 0 to UBound($Transitions, 1) - 1
	  If ($Transitions[$i][0] = $CurrentState) And ($Transitions[$i][1] = $NextState) And (Call($Transitions[$i][2]) = True) Then
		 $CurrentState = $NextState
		 Return
	  EndIf
   Next
EndFunc
State machines are easy to maintain and to extend, as also produce a very easy to understand code
Thanks man for advice, i am new in this kind of programing so any advice is useful for me.

I know bots are not easy to make because it require more actions to work, and as i see on many examples and read on many forums its not easy in AutoIt because it don't support multithreading and multitasking.