Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ; Perfect Roll v2 (written by idle in its leisure time)
- ; Roll abilities for Baldur's Gate character generation
- ; v1.0.0 - initial release
- ; v1.0.1 - removed increment of max_iter (useless feature)
- ; v1.0.2 - replaced for-loop delay with Sleep()
- ; v1.0.3 - added support for "Baldur's Gate II - Shadows of Amn"
- ; name of ini file
- global const $ini = "proll2.ini"
- ; detect missing ini values
- global const $defaultIniValue = -100000000
- Opt ("MustDeclareVars", 1)
- Opt ("MouseCoordMode", 0)
- Opt ("PixelCoordMode", 0)
- Opt ("GUICoordMode", 2)
- Opt ("MouseClickDelay", ReadIniValue ("Common", "mouse_click_delay", 20, 5, 500))
- Opt ("MouseClickDownDelay", ReadIniValue ("Common", "mouse_click_down", 10, 5, 500))
- ; title of baldur's gate window
- global const $bgTitle = ReadIniString ("Common", "window_title", "Tales of the Sword Coast")
- ; title of trash dialog to catch trash keyboard events
- global const $trashTitle = "BG trash can"
- ; primary mouse button ("left", "right" or something else)
- global const $primaryMouseButton = ReadIniString ("Common", "primary_mouse_button", "left")
- ; number of clicks, when it is impossible to test result of clicking
- global $numClicks = 30
- ; delay after mouse click (to wait for update after reroll or '+'/'-')
- global $zeroUpdateDelay = 10
- global $firstUpdateDelay = 1
- global $secondUpdateDelay = 10
- global $tuneEvery = 1
- global $mouseSpeed = 10
- ; how often to update statistics (number of iterations)
- global $updateEvery = 1
- ; learning strength values (fill cache)
- global $learnStrength = 0
- ; limit allowed strength by this value
- global $strengthLimit = 0
- ; limit allowed badness by this value
- global $badnessLimit = 100
- ; number of rolls in a row
- global $rollsNum = 0
- ; delay between different batches of rolls
- global $restTime = 0
- ; maximum ability index
- global const $maxAbility = 5
- ; index of strength ability
- global const $strIndex = 0
- ; coordinates of ability increment buttons
- global $incButton[$maxAbility+1][2]
- ; coordinates of ability decrement buttons
- global $decButton[$maxAbility+1][2]
- ; coordinates of "store", "recall", "reroll" buttons
- global $storeButton[2]
- global $recallButton[2]
- global $rerollButton[2]
- ; coordinates of ability value
- global $abilityRect[$maxAbility+1][4]
- ; coordinates of description text
- global $textRect[4]
- ; coordinates of reserve points value
- global $reserveRect[4]
- ; coordinate of right edge - safe place to play with mouse
- global $rightEdge
- ; priorities of abilities when incrementing
- global const $incAbilityPriority[$maxAbility+1] = [0, 1, 2, 4, 3, 5]
- ; hash of "can not inc/dec" images
- global $canNotInc = -1;
- global $canNotDec = -1;
- ; hash of "0" reserve points
- global $zeroReserve = -1;
- ; hashes of all ability values for all abilities
- global const $maxAbilityValue = 30
- global $abilityChecksum[$maxAbility+1][$maxAbilityValue+1]
- global $abilityChecksumMax[$maxAbility+1] = [-1, -1, -1, -1, -1, -1]
- ; maximum strength is complex value (18/xx)
- global $strIsComplex = 0
- ; control IDs
- global $ctrlMinBadnessLabel
- global $ctrlMinBadnessValue
- global $ctrlMaxStrengthLabel
- global $ctrlMaxStrengthValue
- global $ctrlAvgBadnessLabel
- global $ctrlAvgBadnessValue
- global $ctrlCurBadnessLabel
- global $ctrlCurBadnessValue
- global $ctrlItersNumLabel
- global $ctrlItersNumValue
- global $ctrlIterTimeLabel
- global $ctrlIterTimeValue
- global $ctrlDelaysNumLabel
- global $ctrlDelaysNumValue
- global $ctrlProfilerLabel
- global $ctrlProfilerValue
- global $ctrlHelpValue
- ; current results
- global $minBadness = 1000000
- global $maxStrength = -1000000
- global $totalBadness = 0
- global $curBadness = 1000000
- global $curStrength = -1000000
- global $curAbility[$maxAbility+1]
- global $otherStrength = ""
- global $maxIter = 0
- global $maxTime = 0
- global $curIter = 0
- global $curTime = 0
- global $totalIter = 0
- global $totalTime = 0
- global $bestTime = "never"
- global $begTime
- global $loopBegTime
- global $firstDelaysNum = 0
- global $secondDelaysNum = 0
- HotKeySet ("{PAUSE}")
- HotKeySet ("{PAUSE}", "PauseScript")
- global $paused = 0
- global $recursionDepth = 0
- global $goodWin = 1
- if ((@OSType = "WIN32_NT") and (@OSVersion <> "WIN_NT4")) then
- $goodWin = 1
- else
- $goodWin = 0
- endif
- global $initialized = 0
- ; pause script execution
- func PauseScript ()
- if ($paused) then
- return 0
- endif
- ; avoid nested pause
- $paused = 1
- ; ask user what to do
- local $res
- if ($goodWin) then
- $res = ShowMsgBox (512+32+6, "Script is paused", "Continue running, cancel script or restart reroll process?")
- else
- $res = ShowMsgBox (512+32+2, "Script is paused", "Continue running (ignore), cancel script (abort) or restart reroll process (retry)?")
- endif
- if (($res = 2) or ($res = 3)) then
- ; cancel or abort
- Exit (10)
- elseif (($res = 10) or ($res = 4)) then
- ; try again or retry
- if ($recursionDepth > 30) then
- ShowMsgBox (0, "Warning", "Can not restart due to recursion limit")
- else
- $recursionDepth = $recursionDepth + 1
- ; restore best combination
- Recall ()
- ; recurse deeper
- $paused = 0
- Init ()
- Main ()
- endif
- else
- ; continue or ignore (11 or 5)
- ReadIniFile ()
- endif
- $paused = 0
- endfunc
- global $aborted = 0
- func OnAutoItExit ()
- if ((@ExitMethod = 1) and not ($aborted)) then
- ; exiting by Exit
- Recall ()
- endif
- endfunc
- ; abort execution without cleanup
- func Abort (const $ret)
- $aborted = 1
- Exit ($ret)
- endfunc
- ; fill coordinates
- func FillCoordinates ()
- local $delta
- ; set default coordinates for "Baldur's Gate"
- $incButton[0][0] = 434
- $incButton[0][1] = 142
- $decButton[0][0] = 456
- $decButton[0][1] = 142
- $abilityRect[0][0] = 380
- $abilityRect[0][1] = 135
- $abilityRect[0][2] = 410
- $abilityRect[0][3] = 148
- $delta = 31
- local $i
- for $i = 1 to $maxAbility
- $incButton[$i][0] = $incButton[$i-1][0]
- $incButton[$i][1] = $incButton[$i-1][1] + $delta
- $decButton[$i][0] = $decButton[$i-1][0]
- $decButton[$i][1] = $decButton[$i-1][1] + $delta
- $abilityRect[$i][0] = $abilityRect[$i-1][0]
- $abilityRect[$i][1] = $abilityRect[$i-1][1] + $delta
- $abilityRect[$i][2] = $abilityRect[$i-1][2]
- $abilityRect[$i][3] = $abilityRect[$i-1][3] + $delta
- next
- $storeButton[0] = 230
- $storeButton[1] = 332
- $recallButton[0] = 324
- $recallButton[1] = 332
- $rerollButton[0] = 418
- $rerollButton[1] = 332
- $reserveRect[0] = 431
- $reserveRect[1] = 107
- $reserveRect[2] = 465
- $reserveRect[3] = 122
- $textRect[0] = 180
- $textRect[1] = 356
- $textRect[2] = 400
- $textRect[3] = 362
- $rightEdge = 490
- ; set coordinates for different versions of the game
- switch ($bgTitle)
- case "Baldur's Gate"
- ; coordinates for "Baldur's Gate" were already set
- case "Baldur's Gate II - Shadows of Amn"
- ; override coordinates for "Baldur's Gate II - Shadows of Amn"
- $incButton[0][0] = 291
- $incButton[0][1] = 235
- $decButton[0][0] = 312
- $decButton[0][1] = 235
- $abilityRect[0][0] = 222
- $abilityRect[0][1] = 226
- $abilityRect[0][2] = 262
- $abilityRect[0][3] = 245
- $delta = 31
- local $i
- for $i = 1 to $maxAbility
- $incButton[$i][0] = $incButton[$i-1][0]
- $incButton[$i][1] = $incButton[$i-1][1] + 31 + (1 - Mod ($i, 2)) * 3
- $decButton[$i][0] = $decButton[$i-1][0]
- $decButton[$i][1] = $decButton[$i-1][1] + 31 + (1 - Mod ($i, 2)) * 3
- $abilityRect[$i][0] = $abilityRect[$i-1][0]
- $abilityRect[$i][1] = $abilityRect[$i-1][1] + 31 + (1 - Mod ($i, 2)) * 3
- $abilityRect[$i][2] = $abilityRect[$i-1][2]
- $abilityRect[$i][3] = $abilityRect[$i-1][3] + 31 + (1 - Mod ($i, 2)) * 3
- next
- $storeButton[0] = 82
- $storeButton[1] = 435
- $recallButton[0] = 186
- $recallButton[1] = 435
- $rerollButton[0] = 284
- $rerollButton[1] = 435
- $reserveRect[0] = 276
- $reserveRect[1] = 177
- $reserveRect[2] = 313
- $reserveRect[3] = 194
- $textRect[0] = 371
- $textRect[1] = 180
- $textRect[2] = 590
- $textRect[3] = 200
- $rightEdge = 345
- case else
- endswitch
- endfunc
- ; profiling support
- global $useProfiler = 0
- global const $profilerPointsMax = 100
- global $profilerPoint[$profilerPointsMax+1]
- global $profilerText[$profilerPointsMax+1]
- global $profilerPointNext = 0
- global $profilerTimer
- func ProfilerStart ()
- if ($useProfiler) then
- $profilerTimer = TimerInit ()
- $profilerPointNext = 0
- endif
- endfunc
- func ProfilerAddPoint (const $text)
- if ($useProfiler and ($profilerPointNext < $profilerPointsMax)) then
- $profilerPoint[$profilerPointNext] = TimerDiff ($profilerTimer)
- $profilerText[$profilerPointNext] = $text
- $profilerPointNext = $profilerPointNext + 1
- endif
- endfunc
- func ProfilerGetPoints ()
- return $profilerPointNext
- endfunc
- func ProfilerGetPoint (const $index)
- return $profilerPoint[$index]
- endfunc
- func ProfilerGetDiff (const $index)
- if ($index > 0) then
- return ($profilerPoint[$index] - $profilerPoint[$index-1])
- else
- return $profilerPoint[$index]
- endif
- endfunc
- func ProfilerGetText (const $index)
- return $profilerText[$index]
- endfunc
- func ProfilerToString (const $value)
- return String (Int ($value * 100) / 100)
- endfunc
- func ProfilerGetString (const $index)
- return ProfilerGetText ($index) & "=" & ProfilerToString (ProfilerGetDiff ($index))
- endfunc
- ; save statistics to file
- global $saveStatistics = 0
- global $statsFilename = ""
- global $statsFile = -1
- func StatsOpen (const $prefix)
- if (not ($saveStatistics)) then
- return 0
- endif
- local $filename = $prefix
- local $i
- for $i = 0 to $maxAbility
- if ($filename <> "") then
- $filename = $filename & "-"
- endif
- $filename = $filename & String ($abilityChecksumMax[$i] + 1)
- next
- $filename = $filename & ".log"
- if ($statsFile <> -1) then
- if ($filename = $statsFilename) then
- return
- endif
- FileClose ($statsFile)
- endif
- $statsFile = FileOpen ($filename, 1)
- $statsFilename = $filename
- endfunc
- func StatsWrite ()
- if ($statsFile <> -1) then
- local $str = ""
- local $i
- for $i = 0 to $maxAbility
- if ($i > 0) then
- $str = $str & " "
- endif
- $str = $str & String ($curAbility[$i])
- next
- $str = $str & " " & String ($curBadness)
- if ($curStrength <> -1) then
- $str = $str & " " & String ($curStrength)
- endif
- if (not (FileWriteLine ($statsFile, $str))) then
- FileClose ($statsFile)
- $statsFile = -1
- endif
- endif
- endfunc
- ; small delay in a loop
- func SmallDelay (const $delay)
- Sleep ($delay)
- return $delay
- ;local $i
- ;local $j = 0
- ;for $i = 0 to $delay
- ; $j = $j + $i
- ;next
- ;return $j
- endfunc
- ; click mouse without delay
- func MouseClickNoDelay (const $x, const $y)
- MouseClick ($primaryMouseButton, $x, $y, 1, $mouseSpeed)
- endfunc
- ; click mouse with delay
- ;func MouseClickDelay (const $x, const $y)
- ; MouseClick ($primaryMouseButton, $x, $y, 1, $mouseSpeed)
- ; SmallDelay ($zeroUpdateDelay + $firstUpdateDelay)
- ; Sleep ($secondUpdateDelay)
- ;endfunc
- ; store combination
- func Store ()
- MouseClickNoDelay ($storeButton[0], $storeButton[1])
- MouseClickNoDelay ($storeButton[0], $storeButton[1])
- endfunc
- ; recall combination
- func Recall ()
- MouseClickNoDelay ($recallButton[0], $recallButton[1])
- MouseClickNoDelay ($recallButton[0], $recallButton[1])
- ; try to make better combination
- DecAll ()
- IncAll ()
- endfunc
- ; reroll combination
- func Reroll ()
- ProfilerAddPoint ("1")
- local $checksum = PixelChecksum ($abilityRect[0][0], $abilityRect[0][1], $abilityRect[$maxAbility][2], $abilityRect[$maxAbility][3])
- ProfilerAddPoint ("2")
- MouseClickNoDelay ($rerollButton[0], $rerollButton[1])
- ProfilerAddPoint ("3")
- SmallDelay ($zeroUpdateDelay)
- if (PixelChecksum ($abilityRect[0][0], $abilityRect[0][1], $abilityRect[$maxAbility][2], $abilityRect[$maxAbility][3]) = $checksum) then
- ; wait a little and hope that values will be updated
- SmallDelay ($firstUpdateDelay)
- $firstDelaysNum = $firstDelaysNum + 1
- if (PixelChecksum ($abilityRect[0][0], $abilityRect[0][1], $abilityRect[$maxAbility][2], $abilityRect[$maxAbility][3]) = $checksum) then
- ; wait some more to make sure that values are updated
- Sleep ($secondUpdateDelay)
- $secondDelaysNum = $secondDelaysNum + 1
- endif
- endif
- ProfilerAddPoint ("4")
- endfunc
- ; increase one ability once
- func IncOnce (const $index)
- MouseClickNoDelay ($incButton[$index][0], $incButton[$index][1])
- endfunc
- ; increase one ability to the max
- func IncOne (const $index)
- if (GetReserveChecksum () = $zeroReserve) then
- return
- endif
- local $checksum = GetAbilityChecksum ($index)
- if (GetTextChecksum () = $canNotInc) then
- IncOnce ($index)
- Sleep ($secondUpdateDelay)
- endif
- IncOnce ($index)
- SmallDelay ($zeroUpdateDelay)
- if (GetAbilityChecksum ($index) = $checksum) then
- SmallDelay ($firstUpdateDelay)
- if (GetAbilityChecksum ($index) = $checksum) then
- Sleep ($secondUpdateDelay)
- endif
- endif
- local $i
- for $i = 0 to $numClicks
- if (GetReserveChecksum () = $zeroReserve) then
- exitloop
- endif
- if ($abilityChecksumMax[$index] < 0) then
- if (GetTextChecksum () = $canNotInc) then
- exitloop
- endif
- else
- if (GetAbilityBadness ($index) = 0) then
- exitloop
- endif
- endif
- IncOnce ($index)
- next
- endfunc
- ; increase all abilities
- func IncAll ()
- local $a
- for $a = 0 to $maxAbility
- IncOne ($incAbilityPriority[$a])
- next
- endfunc
- ; decrease one ability once
- func DecOnce (const $index)
- MouseClickNoDelay ($decButton[$index][0], $decButton[$index][1])
- endfunc
- ; decrease one ability to the min
- func DecOne (const $index)
- if (($abilityChecksumMax[$index] >= 0) and (GetAbilityChecksum ($index) = $abilityChecksum[$index][$abilityChecksumMax[$index]])) then
- return
- endif
- local $checksum = GetAbilityChecksum ($index)
- if (GetTextChecksum () = $canNotDec) then
- DecOnce ($index)
- Sleep ($secondUpdateDelay)
- endif
- DecOnce ($index)
- SmallDelay ($zeroUpdateDelay)
- if (GetAbilityChecksum ($index) = $checksum) then
- SmallDelay ($firstUpdateDelay)
- if (GetAbilityChecksum ($index) = $checksum) then
- Sleep ($secondUpdateDelay)
- endif
- endif
- local $i
- for $i = 0 to $numClicks
- if ($abilityChecksumMax[$index] < 0) then
- if (GetTextChecksum () = $canNotDec) then
- exitloop
- endif
- else
- if (GetAbilityChecksum ($index) = $abilityChecksum[$index][$abilityChecksumMax[$index]]) then
- exitloop
- endif
- endif
- DecOnce ($index)
- next
- endfunc
- ; decrease all abilities
- func DecAll ()
- local $a
- for $a = 0 to $maxAbility
- DecOne ($a)
- next
- endfunc
- ; decrease all abilities except strength
- func DecAllButStr ()
- local $a
- for $a = $maxAbility to 1 step -1
- DecOne ($a)
- next
- endfunc
- ; get checksum for selected ability points
- func GetAbilityChecksum (const $index)
- return PixelChecksum ($abilityRect[$index][0], $abilityRect[$index][1], $abilityRect[$index][2], $abilityRect[$index][3])
- endfunc
- ; get checksum for description text
- func GetTextChecksum ()
- return PixelChecksum ($textRect[0], $textRect[1], $textRect[2], $textRect[3])
- endfunc
- ; get checksum for reserve points
- func GetReserveChecksum ()
- return PixelChecksum ($reserveRect[0], $reserveRect[1], $reserveRect[2], $reserveRect[3])
- endfunc
- global $bgNotFound = 0
- global $guiDelay = 500
- ; show baldur's gate window
- func ShowBG ()
- while (not (WinActive ($bgTitle)))
- if ($bgNotFound) then
- exitloop
- endif
- if (not (WinExists ($bgTitle))) then
- $bgNotFound = 1
- ShowMsgBox (0, "Fatal Error", "Window '" & $bgTitle & "' is not active")
- Abort (1)
- endif
- WinActivate ($bgTitle)
- wend
- Sleep ($guiDelay)
- endfunc
- func CaptureTrashEvent ()
- endfunc
- ; save baldur's gate window from receiving unwanted keyboard events, by making trash window active
- func SaveBG (const $useDelay)
- if ($useDelay) then
- ; after message box is shown - avoid enter/esc/space events
- HotKeySet ("{ENTER}", "CaptureTrashEvent")
- HotKeySet ("{ESC}", "CaptureTrashEvent")
- HotKeySet ("{SPACE}", "CaptureTrashEvent")
- endif
- GUISetState (@SW_SHOW)
- while (not (WinActive ($trashTitle)))
- if (not (WinExists ($trashTitle))) then
- Abort (20)
- endif
- WinActivate ($trashTitle)
- wend
- if ($useDelay) then
- Sleep ($guiDelay)
- endif
- if (not ($useDelay)) then
- ; before message box - allow enter/esc/space events
- HotKeySet ("{ENTER}")
- HotKeySet ("{ESC}")
- HotKeySet ("{SPACE}")
- endif
- endfunc
- global $badHashes = ""
- ; get checksums for common images (that does not change for different character)
- func GetCommonChecksums ()
- local $tmp[2]
- ; invalidate hash tables
- InvalidateAbilityChecksumHashes ()
- InvalidateStrengthHash ()
- ; reset to allow default DecOne behaviour
- for $a = 0 to $maxAbility
- $abilityChecksumMax[$a] = -1
- next
- ; get checksum for 'can not decrease'
- while (1)
- DecOne ($maxAbility-1)
- $tmp[0] = GetTextChecksum ()
- DecOne ($maxAbility)
- $tmp[1] = GetTextChecksum ()
- if ($tmp[0] = $tmp[1]) then
- $canNotDec = $tmp[0]
- exitloop
- endif
- wend
- ; reserve all points
- DecAllButStr ()
- ; get checksum for 'can not increase'
- while (1)
- IncOne (0)
- $tmp[0] = GetTextChecksum ()
- DecOne (0)
- IncOne (1)
- $tmp[1] = GetTextChecksum ()
- DecOne (1)
- if ($tmp[0] = $tmp[1]) then
- $canNotInc = $tmp[0]
- exitloop
- endif
- wend
- ; get checksum for reserve = 0
- IncAll ()
- ; just wait enough time
- SmallDelay ($zeroUpdateDelay + $firstUpdateDelay)
- Sleep ($secondUpdateDelay)
- $zeroReserve = GetReserveChecksum ()
- endfunc
- global const $abiHashMaxSize = 200
- global $abiHash[$maxAbility+1][$abiHashMaxSize]
- global $abiHashSize[$maxAbility+1]
- global $useHashTable = 1
- ; invalidate ability checksum hashes
- func InvalidateAbilityChecksumHashes ()
- local $a
- for $a = 0 to $maxAbility
- $abiHashSize[$a] = 0
- next
- endfunc
- ; rehash one ability checksum table, using specified hash table size
- func RehashAbilityChecksum (const $index, const $size)
- local $i
- ; clear hash table
- for $i = 0 to ($size - 1)
- $abiHash[$index][$i] = -1
- next
- $abiHashSize[$index] = 0
- ; fill hash with all checksums
- for $i = 0 to $abilityChecksumMax[$index]
- local $hash = Mod ($abilityChecksum[$index][$i], $size)
- if ($abiHash[$index][$hash] <> -1) then
- return 0
- endif
- $abiHash[$index][$hash] = $i
- next
- $abiHashSize[$index] = $size
- return 1
- endfunc
- ; rehash ability checksums
- func RehashAbilityChecksums ()
- if (not ($useHashTable)) then
- return
- endif
- local $a
- for $a = 0 to $maxAbility
- ; invalidate hash table
- $abiHashSize[$a] = 0
- if ($abilityChecksumMax[$a] >= 0) then
- ; try to fill checksums into hash tables with different sizes
- local $size
- for $size = 1 to $abiHashMaxSize
- if (RehashAbilityChecksum ($a, $size) = 1) then
- ; hash table is valid
- exitloop
- endif
- next
- endif
- next
- endfunc
- ; get ability badness using hash table
- func GetAbilityBadnessByHash (const $index, const $checksum)
- local $i
- if ($abiHashSize[$index] > 0) then
- ; hash table is valid
- $i = $abiHash[$index][Mod ($checksum, $abiHashSize[$index])]
- if ($i >= 0) then
- if ($abilityChecksum[$index][$i] = $checksum) then
- return $i
- endif
- endif
- endif
- ; not found
- return -1
- endfunc
- ; cache of complex strength values
- global $strengthCacheSize = 1000
- global $strengthCacheNext = 0
- global $strengthCache[$strengthCacheSize+1][2]
- ; load strength checksums from ini file
- func LoadStrengthCache ()
- $strengthCacheNext = ReadIniValue ("Cache", "size", 0, 0, $strengthCacheSize + 1)
- local $i = 0
- while ($i < $strengthCacheNext)
- $strengthCache[$i][0] = ReadIniValue ("Cache", "str" & String ($i), -1, -1, -1)
- $strengthCache[$i][1] = ReadIniValue ("Cache", "val" & String ($i), -1, 0, 100)
- $i = $i + 1
- wend
- endfunc
- ; hash table for strength checksums
- global const $strHashMaxSize = 4 * $strengthCacheSize
- global $strHash[$strHashMaxSize]
- global $strHashSize
- func InvalidateStrengthHash ()
- $strHashSize = 0
- endfunc
- ; rehash strength hash table for specified size
- func RehashStrengthSize (const $size)
- local $i
- ; invalidate strength hash
- for $i = 0 to ($size - 1)
- $strHash[$i] = -1
- next
- $strHashSize = 0
- ; fill strength hash
- $i = 0
- while ($i < $strengthCacheNext)
- local $hash = Mod ($strengthCache[$i][0], $size)
- if ($strHash[$hash] >= 0) then
- ; two hashes collide
- return 0
- endif
- $strHash[$hash] = $i
- $i = $i + 1
- wend
- ; hash table is valid
- $strHashSize = $size
- return 1
- endfunc
- ; rehash strength hash table
- func RehashStrength ()
- if (not ($useHashTable)) then
- return
- endif
- for $size = $strengthCacheNext to $strHashMaxSize
- if (RehashStrengthSize ($size) = 1) then
- ; this hash table is valid
- exitloop
- endif
- next
- endfunc
- ; get strength by checksum using hash table
- func GetStrengthByHash (const $checksum)
- local $i
- if ($strHashSize > 0) then
- ; hash table is valid
- $i = $strHash[Mod ($checksum, $strHashSize)]
- if ($i >= 0) then
- if ($strengthCache[$i][0] = $checksum) then
- return $strengthCache[$i][1]
- endif
- endif
- endif
- ; not found
- return -1
- endfunc
- ; get checksums for everything that matters
- func GetAllChecksums ()
- local $a
- local $i
- ; invalidate hash tables
- InvalidateAbilityChecksumHashes ()
- InvalidateStrengthHash ()
- DecAllButStr ()
- ; get checksums for all values of each ability
- for $a = 0 to $maxAbility
- ; increase current ability to max
- IncOne ($a)
- ; scan all values from max to min
- local $checksum = GetAbilityChecksum ($a)
- $i = 0
- while (1)
- ; save checksum for current ability value
- $abilityChecksum[$a][$i] = $checksum
- DecOnce ($a)
- if (GetTextChecksum () = $canNotDec) then
- ; no more abilities (if text did not change yet, we'll get there on next iteration)
- exitloop
- endif
- SmallDelay ($zeroUpdateDelay)
- if (GetAbilityChecksum ($a) = $checksum) then
- ; wait just a moment
- SmallDelay ($firstUpdateDelay)
- if (GetAbilityChecksum ($a) = $checksum) then
- ; ability value did not change - try to wait some more
- Sleep ($secondUpdateDelay)
- endif
- endif
- $checksum = GetAbilityChecksum ($a)
- if ($abilityChecksum[$a][$i] = $checksum) then
- ; ability value still did not change, probably button did not work
- continueloop
- endif
- ; go to the next value
- $i = $i + 1
- wend
- ; number of distinct ability values
- $abilityChecksumMax[$a] = $i
- next
- if ($learnStrength) then
- if (ShowMsgBox (1, "Warning", "Running in learning mode (without optimization of abilities). " _
- & "Set 'learn_strength=0' in '" & $ini & "' file if you want to run in normal mode.") <> 1) then
- Abort (20)
- endif
- $strIsComplex = 1
- else
- ; ask user if strength is a complex value, i.e. 18/xx
- IncOne ($strIndex)
- if (ShowMsgBox (4, "Answer me", "Is STRength complex value, like 18/xx? You can always say NO if you don't care about strength") = 6) then
- $strIsComplex = 1
- else
- $strIsComplex = 0
- endif
- endif
- ; calculate hash tables
- RehashAbilityChecksums ()
- RehashStrength ()
- ; calculate hash tables status
- $badHashes = ""
- for $i = 0 to $maxAbility
- if (StringLen ($badHashes) > 0) then
- $badHashes = $badHashes & ","
- endif
- if ($abiHashSize[$i] > 0) then
- $badHashes = $badHashes & "-"
- else
- $badHashes = $badHashes & String ($i)
- endif
- next
- if ($strHashSize > 0) then
- $badHashes = $badHashes & ",-"
- else
- $badHashes = $badHashes & ",s"
- endif
- endfunc
- ; find current ability value badness (how far is it from maximum)
- func GetAbilityBadness (const $index)
- local $checksum = GetAbilityChecksum ($index)
- local $i
- ; get badness using hash table
- $i = GetAbilityBadnessByHash ($index, $checksum)
- if ($i >= 0) then
- return $i
- endif
- ; use linear search
- for $i = 0 to $abilityChecksumMax[$index]
- if ($abilityChecksum[$index][$i] = $checksum) then
- return $i
- endif
- next
- if ($index = $strIndex) then
- ; we probably have unknown strength value - this is goodness
- ; treat strength in this way even if user said it is not complex
- return 0
- endif
- ; probably something has obscured BG window
- ShowMsgBox (0, "Fatal Error", "Can not parse ability value")
- Exit (2)
- endfunc
- ; read string from ini file
- func ReadIniString (const $section, const $name, const $def)
- local $tmp = "abrakadabra default value (don't use it)"
- local $str = IniRead ($ini, $section, $name, $tmp)
- if ($str = $tmp) then
- IniWrite ($ini, $section, $name, $def)
- return $def
- endif
- return $str
- endfunc
- ; read integer value from ini file
- func ReadIniValue (const $section, const $name, const $def, const $min, const $max)
- local $val = Int (IniRead ($ini, $section, $name, String ($defaultIniValue)))
- local $ret = $val
- if ($val = $defaultIniValue) then
- $ret = $def
- endif
- if (($min <> -1) and ($ret < $min)) then
- $ret = $min
- endif
- if (($max <> -1) and ($ret > $max)) then
- $ret = $max
- endif
- if ($ret <> $val) then
- IniWrite ($ini, $section, $name, String ($ret))
- endif
- return $ret
- endfunc
- ; get current strength from cache or user
- func GetCurrentStrength ()
- local $str = GetAbilityChecksum ($strIndex)
- local $val
- local $i = 0
- $val = GetStrengthByHash ($str)
- if ($val >= 0) then
- return $val
- endif
- while ($i < $strengthCacheNext)
- if ($strengthCache[$i][0] = $str) then
- return $strengthCache[$i][1]
- endif
- $i = $i + 1
- wend
- while (1)
- ; ask user for the value
- $val = ShowInputBox ("Answer me", "Enter current strength value", "", 0, 100)
- if ($val = 0) then
- $val = 100
- endif
- ; test that entered value is not used already
- $i = 0
- while ($i < $strengthCacheNext)
- if ($strengthCache[$i][1] = $val) then
- exitloop
- endif
- $i = $i + 1
- wend
- if ($i <> $strengthCacheNext) then
- if (ShowMsgBox (4, "Warning", "Strength value " & String ($val) & " is already cached. Would you like to reenter strength value?") = 6) then
- continueloop
- endif
- endif
- exitloop
- wend
- ; add value to the cache
- if ($strengthCacheNext <= $strengthCacheSize) then
- $strengthCache[$strengthCacheNext][0] = $str
- $strengthCache[$strengthCacheNext][1] = $val
- ; save value in ini file
- IniWrite ($ini, "Cache", "str" & String ($strengthCacheNext), String ($str))
- IniWrite ($ini, "Cache", "val" & String ($strengthCacheNext), String ($val))
- ; save cache size
- $strengthCacheNext = $strengthCacheNext + 1
- IniWrite ($ini, "Cache", "size", String ($strengthCacheNext))
- ; note: don't rehash strength on the fly, wait until user restarts the process
- endif
- return $val
- endfunc
- ; maximize strength with minimum movements
- func MaximizeStrength ()
- local $pointsNeeded = GetAbilityBadness ($strIndex)
- local $i
- if ($pointsNeeded <> 0) then
- ; minimize mouse movements
- local $bestv = 1000000
- local $besti = -1
- for $i = 0 to $maxAbility
- if ($i = $strIndex) then
- continueloop
- endif
- local $tempv = $abilityChecksumMax[$i] - GetAbilityBadness ($i)
- if ($tempv = $pointsNeeded) then
- $bestv = $tempv
- $besti = $i
- exitloop
- endif
- if (($tempv > $pointsNeeded) and ($tempv < $bestv)) then
- $bestv = $tempv
- $besti = $i
- endif
- next
- if ($besti >= 0) then
- ; found ability with minimum difference from needed amount
- DecOne ($besti)
- IncOne ($strIndex)
- endif
- endif
- if (GetAbilityBadness ($strIndex) <> 0) then
- ; just minimize all abilities in turn
- for $i = 0 to $maxAbility
- if ($i = $strIndex) then
- continueloop
- endif
- DecOne ($i)
- IncOne ($strIndex)
- if (GetAbilityBadness ($strIndex) = 0) then
- exitloop
- endif
- next
- if (GetAbilityBadness ($strIndex) <> 0) then
- ShowMsgBox (0, "Fatal Error", "Can not maximize strength")
- Abort (4)
- endif
- endif
- endfunc
- ; number of strength lookups after last good combination
- global $strengthLookups = 0
- ; test current combination
- func TestCurrentCombination ()
- local $i
- ; make sure that all points are distributed (this is not necessary now)
- ;for $i = 0 to 3
- ; if (GetReserveChecksum () = $zeroReserve) then
- ; exitloop
- ; endif
- ;
- ; if ($i = 3) then
- ; ShowMsgBox (0, "Fatal Error", "Can not distribute all points")
- ; Exit (3)
- ; endif
- ;
- ; IncAll ()
- ;next
- ; get badness for all abilities
- $curBadness = 0
- $curStrength = -1
- for $i = 0 to $maxAbility
- $curAbility[$i] = GetAbilityBadness ($i)
- $curBadness = $curBadness + $curAbility[$i]
- next
- if ($curBadness > $minBadness) then
- ; badness is worse
- return 0
- endif
- if (not ($strIsComplex)) then
- if ($curBadness = $minBadness) then
- ; badness is the same and strength does not matter
- return 0
- endif
- ; badness is not as bad
- $minBadness = $curBadness
- ; save time of best combination
- $bestTime = String (@MDAY) & "/" & String (@MON) & " " & String (@HOUR) & ":" & String (@MIN) & ":" & String (@SEC)
- return 1
- endif
- if (not ($learnStrength)) then
- if ($curBadness > $badnessLimit) then
- ; badness is not good enough
- return 0
- endif
- if (($curBadness = $minBadness) and $strIsComplex and ($maxStrength >= 100)) then
- ; don't look for strength if strength is at the maximum for this badness already
- return 0
- endif
- endif
- ; test that strength is better
- $strengthLookups = $strengthLookups + 1
- ; increment strength to the maximum
- MaximizeStrength ()
- $curStrength = GetCurrentStrength ()
- if (($curBadness = $minBadness) or $learnStrength) then
- ; badness is the same, or we are learning strength checksums
- ; add strength to the list
- ; (I'd like to see all other combinations with current badness)
- if (StringLen ($otherStrength) >= 18) then
- $i = StringInStr ($otherStrength, ",", 1, 1)
- if ($i > 0) then
- $otherStrength = StringMid ($otherStrength, $i + 1)
- endif
- endif
- if (StringLen ($otherStrength) > 0) then
- $otherStrength = $otherStrength & ","
- endif
- $otherStrength = $otherStrength & String ($curStrength)
- if ($learnStrength) then
- ; just show current strength
- $maxStrength = $curStrength
- return 0
- endif
- if ($curStrength <= $maxStrength) then
- ; strength is not better
- return 0
- endif
- endif
- if (($curStrength < $strengthLimit) and ($curBadness < $badnessLimit)) then
- ; current strength is not good enough
- ; throw away, even if badness is not bad
- return 0
- endif
- ; got better combination
- if ($curBadness < $minBadness) then
- ; set list of strengths for new badness
- $otherStrength = String ($curStrength)
- endif
- ; save time of best combination
- $bestTime = String (@MDAY) & "/" & String (@MON) & " " & String (@HOUR) & ":" & String (@MIN) & ":" & String (@SEC)
- $minBadness = $curBadness
- $maxStrength = $curStrength
- return 1
- endfunc
- ; reset loop variables
- func ResetLoop ()
- $minBadness = 1000000
- $maxStrength = -1000000
- $totalBadness = 0
- $curBadness = 1000000
- $curStrength = -1000000
- $otherStrength = ""
- $strengthLookups = 0
- $bestTime = "never"
- $curIter = 0
- $totalIter = 0
- $curTime = 0
- $begTime = TimerInit ()
- $loopBegTime = $begTime
- $firstDelaysNum = 0
- $secondDelaysNum = 0
- StatsOpen (ReadIniString ("Common", "stats_prefix", "stats"))
- endfunc
- ; main loop
- func MainLoop ()
- local $accFirstDelaysNum = 0
- local $accSecondDelaysNum = 0
- local $updated = 0
- ; increase all abilities (to avoid bad average interval on start)
- IncAll ()
- ; reset loop variables
- ResetLoop ()
- while (1)
- ProfilerStart ()
- $updated = 0
- if (TestCurrentCombination () <> 0) then
- ; have some improvement - reset number of iterations
- $curIter = 0
- ; note: don't reset timer
- ; store current combination
- Store ()
- $updated = 1
- $strengthLookups = 0
- endif
- ; save statistics
- StatsWrite ()
- ;ProfilerAddPoint ("t")
- ; accumulate statistics
- $totalBadness = $totalBadness + $curBadness
- $curIter = $curIter + 1
- $totalIter = $totalIter + 1
- $curTime = TimerDiff ($begTime)
- $totalTime = TimerDiff ($loopBegTime)
- if ($updated or (Mod ($totalIter, $updateEvery) = 0)) then
- ; output current progress
- UpdateStatisticsData ()
- endif
- ; test that best combination was reached
- if ($minBadness = 0) then
- if (not ($strIsComplex)) then
- return 1
- endif
- if ($maxStrength >= 100) then
- return 1
- endif
- endif
- ; test for limit by iterations
- if (($maxIter > 0) and ($curIter >= $maxIter)) then
- ; ask for more
- switch (ShowMsgBox (3, "Answer me", "No improvements in last " & String ($curIter) & " iterations, stop now?"))
- case 6
- ; stop
- return 0
- case 7
- ; don't stop
- case else
- ; cancel script
- Exit (0)
- endswitch
- ; restart limited iterations
- $curIter = 0
- $curTime = 0
- ; restart timer
- $begTime = TimerInit ()
- endif
- ; test for limit by time
- if (($maxTime > 0) and ($curTime >= ($maxTime * 1000))) then
- ; ask for more
- switch (ShowMsgBox (3, "Answer me", "More than " & String ($maxTime) & " seconds passed, stop now?"))
- case 6
- ; stop
- return 0
- case 7
- ; don't stop
- case else
- ; cancel script
- Exit (0)
- endswitch
- ; restart limited iterations
- $curIter = 0
- $curTime = 0
- ; restart timer
- $begTime = TimerInit ()
- endif
- ;ProfilerAddPoint ("n")
- ; reroll combination
- Reroll ()
- ;ProfilerAddPoint ("e")
- ; tune delays
- ; try to increase delays every iteration
- if ($secondDelaysNum > 0) then
- $firstUpdateDelay = $firstUpdateDelay + 1
- $accSecondDelaysNum = $accSecondDelaysNum + $secondDelaysNum
- $secondDelaysNum = 0
- endif
- if ($firstDelaysNum > 0) then
- $zeroUpdateDelay = $zeroUpdateDelay + 1
- $accFirstDelaysNum = $accFirstDelaysNum + $firstDelaysNum
- $firstDelaysNum = 0
- endif
- ; try to decrease delays every N-th iteration
- if (Mod ($totalIter, $tuneEvery) = 0) then
- ; try to tune delays quickly, if every iteration end up with delay
- if ($accFirstDelaysNum = $tuneEvery) then
- $zeroUpdateDelay = $zeroUpdateDelay + 2 * $tuneEvery
- endif
- if ($accSecondDelaysNum = $tuneEvery) then
- $firstUpdateDelay = $firstUpdateDelay + 2 * $tuneEvery
- endif
- if (($accFirstDelaysNum > 0) and ($accSecondDelaysNum = 0) and ($firstUpdateDelay > 0)) then
- ; there were first delays, but no second delays - can decrease first delay
- $firstUpdateDelay = $firstUpdateDelay - 1
- endif
- if (($accFirstDelaysNum = 0) and ($zeroUpdateDelay > 0)) then
- ; there were no first delays - can decrease zero delay
- $zeroUpdateDelay = $zeroUpdateDelay - 1
- endif
- ; reset accumulated delay hits
- $accFirstDelaysNum = 0
- $accSecondDelaysNum = 0
- endif
- UpdateProfilerData ()
- if (($rollsNum > 0) and (Mod ($totalIter, $rollsNum) = 0)) then
- ; have some rest after specified number of rolls
- local $timer = TimerInit ()
- local $x = $rightEdge
- local $y = $rerollButton[1]
- MouseMove ($x, $y)
- while (TimerDiff ($timer) < $restTime)
- $x = $x + Random (-10, 10, 1)
- $y = $y + Random (-10, 10, 1)
- if ($x < $rightEdge) then
- $x = $rightEdge
- endif
- MouseMove ($x, $y)
- wend
- endif
- wend
- endfunc
- ; create dialog with label controls
- func CreateGui ()
- GUICreate ($trashTitle)
- local $cellW = 140
- local $cellH = 20
- $ctrlMinBadnessLabel = GUICtrlCreateLabel ("Minimum impurity: ", 20, 20, $cellW, $cellH)
- $ctrlMinBadnessValue = GUICtrlCreateLabel ("", 0, -1)
- $ctrlMaxStrengthLabel = GUICtrlCreateLabel ("Maximum strength: ", -2 * $cellW, 0)
- $ctrlMaxStrengthValue = GUICtrlCreateLabel ("", 0, -1, $cellW * 2, $cellH)
- $ctrlCurBadnessLabel = GUICtrlCreateLabel ("Current impurity: ", -3 * $cellW, 10, $cellW, $cellH)
- $ctrlCurBadnessValue = GUICtrlCreateLabel ("", 0, -1)
- $ctrlAvgBadnessLabel = GUICtrlCreateLabel ("Average impurity: ", -2 * $cellW, 0)
- $ctrlAvgBadnessValue = GUICtrlCreateLabel ("", 0, -1)
- $ctrlItersNumLabel = GUICtrlCreateLabel ("Iterations num: ", -2 * $cellW, 10)
- $ctrlItersNumValue = GUICtrlCreateLabel ("", 0, -1)
- $ctrlIterTimeLabel = GUICtrlCreateLabel ("Avg iter. time: ", -2 * $cellW, 0)
- $ctrlIterTimeValue = GUICtrlCreateLabel ("", 0, -1)
- $ctrlDelaysNumLabel = GUICtrlCreateLabel ("Update delays: ", -2 * $cellW, 10)
- $ctrlDelaysNumValue = GUICtrlCreateLabel ("", 0, -1)
- $ctrlProfilerLabel = GUICtrlCreateLabel ("Profiler: ", -2 * $cellW, 10, $cellW / 2, $cellH)
- $ctrlProfilerValue = GUICtrlCreateLabel ("", 0, -1, $cellW * 2, $cellH)
- $ctrlHelpValue = GUICtrlCreateLabel ("Press 'PAUSE/BREAK' to pause script", -2 * $cellW - $cellW / 2, 20, 3 * $cellW, $cellH)
- GUISetState (@SW_SHOW)
- endfunc
- ; update statistical information
- func UpdateStatisticsData ()
- GUICtrlSetData ($ctrlMinBadnessValue, String ($minBadness) & " at " & String ($bestTime))
- local $str
- if ($strIsComplex) then
- if (StringLen ($otherStrength) > 0) then
- $str = " (" & $otherStrength & ")"
- else
- $str = ""
- endif
- GUICtrlSetData ($ctrlMaxStrengthValue, String ($maxStrength) & $str & " / " & String ($strengthLookups))
- else
- GUICtrlSetData ($ctrlMaxStrengthValue, "")
- endif
- GUICtrlSetData ($ctrlCurBadnessValue, String ($curBadness))
- if ($totalIter <> 0) then
- local $avg = Int ($totalBadness / $totalIter * 100)
- GUICtrlSetData ($ctrlAvgBadnessValue, String ($avg / 100))
- $avg = Int ($totalTime)
- GUICtrlSetData ($ctrlItersNumValue, String ($totalIter) & " in " & String ($avg / 1000))
- $avg = Int ($totalTime / $totalIter)
- GUICtrlSetData ($ctrlIterTimeValue, String ($avg / 1000))
- endif
- GUICtrlSetData ($ctrlDelaysNumValue, String ($zeroUpdateDelay) & "+" & String ($firstUpdateDelay) & " | " & $badHashes)
- endfunc
- ; update profiler information
- func UpdateProfilerData ()
- if ($useProfiler) then
- local $i = 0
- local $str = ""
- while ($i < ProfilerGetPoints ())
- if (StringLen ($str) > 0) then
- $str = $str & ","
- endif
- $str = $str & ProfilerGetString ($i)
- $i = $i + 1
- wend
- GUICtrlSetData ($ctrlProfilerValue, $str)
- endif
- endfunc
- ; ask something from user
- func ShowMsgBox (const $type, const $title, const $text)
- SaveBG (0)
- local $ret = MsgBox ($type, $title, $text)
- SaveBG (1)
- ShowBG ()
- return $ret
- endfunc
- ; ask integer value from user
- func ShowInputBox (const $title, const $text, const $def, const $min, const $max)
- local $ret
- SaveBG (0)
- while (1)
- local $str = InputBox ($title, $text, $def)
- $ret = Int ($str)
- if (($str = "") or ($str <> String ($ret))) then
- ; not a valid integer
- continueloop
- endif
- if (($min <> -1) and ($ret < $min)) then
- ; less than minimum
- continueloop
- endif
- if (($max <> -1) and ($ret > $max)) then
- ; greater than maximum
- continueloop
- endif
- ; value is good
- exitloop
- wend
- SaveBG (1)
- ShowBG ()
- return $ret
- endfunc
- ; read most values from ini file
- func ReadIniFile ()
- $numClicks = ReadIniValue ("Common", "number_of_clicks", 30, 25, 100)
- $zeroUpdateDelay = ReadIniValue ("Common", "zero_update_delay", 20, 0, 10000000)
- $firstUpdateDelay = ReadIniValue ("Common", "first_update_delay", 1, 0, 10000000)
- $secondUpdateDelay = ReadIniValue ("Common", "second_update_delay", 100, 10, 2000)
- $mouseSpeed = ReadIniValue ("Common", "mouse_speed", 5, 0, 100)
- $updateEvery = ReadIniValue ("Common", "update_every", 1, 1, 1000)
- $tuneEvery = ReadIniValue ("Common", "tune_every", 10, 5, 1000000)
- $learnStrength = ReadIniValue ("Common", "learn_strength", 0, 0, 1)
- $strengthLimit = ReadIniValue ("Common", "strength_limit", 0, 0, 100)
- $badnessLimit = ReadIniValue ("Common", "impurity_limit", 100, 0, 1000)
- $rollsNum = ReadIniValue ("Common", "rolls_num", 0, 0, 1000000000)
- $restTime = ReadIniValue ("Common", "rest_time", 0, 0, 60*60)
- $guiDelay = ReadIniValue ("Common", "gui_delay", 1000, 500, 5000)
- $maxIter = ReadIniValue ("Common", "max_iter", 10, 0, 1000000000)
- $maxTime = ReadIniValue ("Common", "max_time", 24*60*60, 0, 400*30*24*60*60)
- $useHashTable = ReadIniValue ("Common", "use_hash_table", 0, 0, 1)
- $useProfiler = ReadIniValue ("Common", "use_profiler", 0, 0, 1)
- $saveStatistics = ReadIniValue ("Common", "save_stats", 0, 0, 1)
- Opt ("MouseClickDelay", ReadIniValue ("Common", "mouse_click_delay", 20, 5, 500))
- Opt ("MouseClickDownDelay", ReadIniValue ("Common", "mouse_click_down", 10, 5, 500))
- endfunc
- func Init ()
- if ($initialized) then
- return 0
- endif
- ; initialize everything
- ReadIniFile ()
- FillCoordinates ()
- LoadStrengthCache ()
- CreateGui ()
- ; show short info
- ShowMsgBox (0, "Roll along", _
- "This utility can be used to roll perfect abilities in Baldur's Gate. " _
- & "Just run it while abilities selection page is on screen. " _
- & "It will do all the work for you, maybe asking some silly questions, " _
- & "like which numbers it has rolled just now (it can not read yet), " _
- & "or whether to continue rolling or not." & Chr (10) _
- & "You can see '" & $trashTitle & "' window on screen - move it aside, such " _
- & "that it will not obscure Baldur's Gate window, but don't close it - " _
- & "it is used to capture trash events left in buffer sometimes " _
- & "(note: to avoid trash events, press buttons in dialogs with mouse). " _
- & "By the way, I should have mentioned that you have to run the game " _
- & "in windowed mode, otherwise this utility will not roll anything." & Chr (10) _
- & "When identifying number for strength, when you see '18/x', type 'x' " _
- & "as a number. For example, 18/03 means 3, while 18/00 means 100 " _
- & "(the best)." & Chr (10) _
- & "Press 'Pause/Break' at any time to stop rolling." & Chr (10) _
- & "Let's have some fun?")
- ; wait until user activates abilities selection page
- while (1)
- local $ret = ShowMsgBox (3, "Answer me", "Abilities selection page is active?")
- if ($ret = 6) then
- exitloop
- endif
- if ($ret <> 7) then
- Abort (30)
- endif
- wend
- ; get checksums for common images
- GetCommonChecksums ()
- $initialized = 1
- endfunc
- func Main ()
- ReadIniFile ()
- while (1)
- ; ask if user wants to roll abilities again
- if (ShowMsgBox (4, "Answer me", "Continue rolling?") <> 6) then
- Abort (0)
- endif
- ; reread all checksums
- GetAllChecksums ()
- ; roll abilities
- MainLoop ()
- ; recall best combination
- Recall ()
- wend
- endfunc
- ; at last, execute everything
- Init ()
- Main ()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement