daily pastebin goal
19%
SHARE
TWEET

Untitled

a guest Jun 18th, 2015 1,607 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ; Path of Exile Item Info Tooltip
  2. ;
  3. ; Version: 1.8.6 (hazydoc / IGN:Sadou)
  4. ;
  5. ; This script was originally based on the POE_iLVL_DPS-Revealer script (v1.2d) found here:
  6. ; https://www.pathofexile.com/forum/view-thread/594346
  7. ;
  8. ; Changes to the POE_iLVL_DPS-Revealer script as recent as it's version 1.4.1 have been
  9. ; brought over. Thank you Nipper4369 and Kislorod!
  10. ;
  11. ; The script has been added to substantially to enable the following features in addition to
  12. ; itemlevel and weapon DPS reveal:
  13. ;
  14. ;   - show total affix statistic for rare items
  15. ;   - show possible min-max ranges for all affixes on rare items
  16. ;   - reveal the combination of difficult compound affixes (you might be surprised what you find)
  17. ;   - show affix ranges for uniques
  18. ;   - show map info (thank you, Kislorod and Necrolis)
  19. ;   - show max socket info (thank you, Necrolis)
  20. ;   - has the ability to convert currency items to chaos orbs (you can adjust the rates by editing
  21. ;     <datadir>\CurrencyRates.txt)
  22. ;   - can show which gems are valuable and/or drop-only (all user adjustable)
  23. ;   - can show a reminder for uniques that are generally considered valuable (user adjustable as well)
  24. ;   - adds a system tray icon and proper system tray description tooltip
  25. ;
  26. ; All of these features are user-adjustable by using a "database" of text files which come
  27. ; with the script and are easy to edit by non developers. See header comments in those files
  28. ; for format infos and data sources.
  29. ;
  30. ; Known issues:
  31. ;    
  32. ;     Even though there have been tons of tests made on composite affix combinations, I expect
  33. ;     there to be edge cases still that may return an invalid or not found affix bracket.
  34. ;     You can see these entries in the affix detail lines if they have the text "n/a" (not available)
  35. ;     somewhere in them or if you see an empty range " - *". The star by the way marks ranges
  36. ;     that have been added together for a guessed attempt as to the composition of a possible
  37. ;     compound affix. If you see this star, take a closer look for a moment to check if the
  38. ;     projection is correct. I expect these edge cases to be properly dealt with over time as the
  39. ;     script matures. For now I'd estimate that at least 80% of the truly hard cases are correctly
  40. ;     identified.
  41. ;
  42. ;     Some background info: because the game concatenates values from multiple affix sources into
  43. ;     one final entry on the ingame tooltip there is no reliable way to work backwards from the
  44. ;     composite value to each individual part. For example, Stun Recovery can be added as suffix if
  45. ;     it contributes alone, but can also be a prefix if it is a composite of Stun Recovery and
  46. ;     Evasion Rating (or others). Because there is one final entry, while prefix and suffix can
  47. ;     appear at the same time and will be added together, you can't reliably reverse engineer which
  48. ;     affix contributed what part of the composite value. This is akin to taking a random source of
  49. ;     numbers, adding them up to one value and then asking someone to work out backwards what the
  50. ;     original source values were.
  51. ;     Similarily, in cases like boosted Stun Recovery (1) and Evasion Rating (2) on an item in difficult
  52. ;     cases there is no 100% reliable way to tell if the prefix "+ Evasion Rating / incr. Stun Recovery"
  53. ;     contributed to both stats at once or if the suffix "+ Stun Recovery" contributed to (1)
  54. ;     and the prefix "+ Evasion Rating" cotributed to (2) or possibly a combination of both.
  55. ;     Often it is possible to make guesses by working your way backwards from both partial affixes, by
  56. ;     looking at the affix bracket ranges and the item level to see what is even possible to be there and
  57. ;     what isn't. In the worst case for a double compound affix, all four ranges will be possible to be
  58. ;     combined.
  59. ;
  60. ;     I have tested the tooltip on many, many items in game from my own stash and from trade chat
  61. ;     and I can say that in the overwhelming majority of cases the tooltip does indeed work correctly.
  62. ;
  63. ;     IMPORTANT: as you may know, the total amount of affixes (w/o implicit mods) can be 6, of which
  64. ;     3 at most are prefixes and likewise 3 at most are suffixes. Be especially weary, then of cases
  65. ;     where this prefix/suffix limit is overcapped. It may happen that the tooltip shows 4 suffixes,
  66. ;     and 3 prefixes total. In this case the most likely explanation is that the script failed to properly
  67. ;     determine composite affixes. Composite affixes ("Comp. Prefix" or "Comp. Suffix" in the tooltip)
  68. ;     are two affix lines on the ingame tooltip that together form one single composite affix.
  69. ;     Edit v1.4: This hasn't happened for a longer time now, but I am leaving this important note in
  70. ;     so end users stay vigilant (assuming anyone even reads this wall of text :)).
  71. ;
  72. ;   - I do not know which affixes are affected by +% Item Quality. Currently I have functions in place
  73. ;     that can boost a range or a single value to adjust for Item Quality but currently these aren't used
  74. ;     much. Partially this is also because it is not easy to tell if out-of-bounds cases are the result
  75. ;     of faulty input data (I initially pulled data from the PoE mods compendium but later made the PoE
  76. ;     homepage the authoritative source overruling data from other sources) or of other unreckognized and
  77. ;     unhandled entities or systems.
  78. ;
  79. ; Todo:
  80. ;
  81. ;   - handle ranges for implicit mods
  82. ;   - find a way to deal with master crafted mods (currently that's a tough one, probably won't be possible)
  83. ;   - show max possible for guesstimated ranges
  84. ;   - de-globalize the script (almost done)
  85. ;   - refactor ParseAffixes into ParseAffixesSimple and ParseAffixesComplex (low priority)
  86. ;
  87. ; Notes:
  88. ;
  89. ;   - Global values marked with an inline comment "d" are globals for debugging so they can be easily
  90. ;     (re-)enabled using global search and replace. Marking variables as global means they will show
  91. ;     up in AHK's Variables and contents view of the script.
  92. ;  
  93. ; Needs AutoHotKey v1.1.05 or later
  94. ;   from http://ahkscript.org and NOT http://www.autohotkey.com
  95. ;   the latter domain was apparently taken over by a for-profit company!
  96. ;
  97. ; Original credits:
  98. ;
  99. ;   mcpower - for the base iLVL display of the script 5months ago before Immo.
  100. ;   Immo - for the base iLVL display of the script.(Which was taken from mcpower.)
  101. ;   olop4444 - for helping me figure out the calculations for Q20 items.
  102. ;   Aeons - for a rewrite and fancy tooltips.
  103. ;   kongyuyu - for base item level display.
  104. ;   Fayted - for testing the script.
  105. ;
  106. ; Original author's comment:
  107. ;
  108. ; If you have any questions or comments please post them there as well. If you think you can help
  109. ; improve this project. I am looking for contributors. So Pm me if you think you can help.
  110. ;
  111. ; If you have a issue please post what version you are using.
  112. ; Reason being is that something that might be a issue might already be fixed.
  113. ;
  114.  
  115. ; Run test suites (see end of script)
  116. ; Note: don't set this to true for normal every day use...
  117. ; This is just for fellow developers.
  118. RunTests := False
  119.  
  120. #SingleInstance force
  121. #NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
  122. #Persistent ; Stay open in background
  123. SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
  124. #Include %A_ScriptDir%\data\Version.txt
  125.  
  126. MsgWrongAHKVersion := "AutoHotkey v" . AHKVersionRequired . " or later is needed to run this script. `n`nYou are using AutoHotkey v" . A_AhkVersion . " (installed at: " . A_AhkPath . ")`n`nPlease go to http://ahkscript.org to download the most recent version."
  127. If (A_AhkVersion <= AHKVersionRequired)
  128. {
  129.     MsgBox, 16, Wrong AutoHotkey Version, % MsgWrongAHKVersion
  130.     ExitApp
  131. }
  132.  
  133. #Include %A_ScriptDir%\data\Messages.txt
  134.  
  135. ; Instead of polluting the default namespace with Globals, create our own Globals "namespace".
  136. class Globals {
  137.    
  138.     Set(name, value) {
  139.         Globals[name] := value
  140.     }
  141.    
  142.     Get(name, value_default="") {
  143.         result := Globals[name]
  144.         If (result == "") {
  145.             result := value_default
  146.         }
  147.         return result
  148.     }
  149. }
  150. Globals.Set("AHKVersionRequired", AHKVersionRequired)
  151. Globals.Set("ReleaseVersion", ReleaseVersion)
  152. Globals.Set("DataDir", A_ScriptDir . "\data")
  153.  
  154.  
  155. class UserOptions {
  156.  
  157.     OnlyActiveIfPOEIsFront := 1     ; Set to 1 to make it so the script does nothing if Path of Exile window isn't the frontmost.
  158.                                     ; If 0, the script also works if PoE isn't frontmost. This is handy for have the script parse
  159.                                     ; textual item representations appearing somewhere else, like in the forums or text files.
  160.  
  161.     ShowItemLevel := 1              ; Show item level and the item type's base level (enabled by default change to 0 to disable)
  162.     ShowMaxSockets := 1             ; Show the max sockets based on ilvl and type
  163.     ShowDamageCalculations := 1     ; Show damage projections (for weapons only)
  164.  
  165.     ShowAffixTotals := 1            ; Show total affix statistics
  166.     ShowAffixDetails := 1           ; Show detailed info about affixes
  167.     ShowAffixLevel := 0             ; Show item level of the affix
  168.     ShowAffixBracket := 1           ; Show range for the affix' bracket as is on the item
  169.     ShowAffixMaxPossible := 1       ; Show max possible bracket for an affix based on the item's item level
  170.     ShowAffixBracketTier := 1       ; Show a T# indicator of the tier the affix bracket is in.
  171.                                     ; T1 being the highest possible, T2 second-to-highest and so on
  172.                                    
  173.     ShowAffixBracketTierTotal := 1  ; Appends the total number of tiers for a given affix in parentheses T/#Total
  174.                                     ; T4/8 would represent the fourth highest tier, in eight total tiers.
  175.  
  176.     TierRelativeToItemLevel := 0    ; When determining the affix bracket tier, take item level into consideration.
  177.                                     ; However, this also means that the lower the item level the less the diversity
  178.                                     ; of possible affix tiers since there aren't as many possibilities. This will
  179.                                     ; give the illusion that a low level item might be really, really good when it
  180.                                     ; has all T1 but in reality it can only have T1 since it's item level is so low
  181.                                     ; it can only ever take the first bracket.
  182.                                     ;
  183.                                     ; If this option is set to 0, the tiers will always display relative to the full
  184.                                     ; range of tiers available, ignoring the item level.
  185.  
  186.     ShowCurrencyValueInChaos := 1   ; Convert the value of currency items into chaos orbs.
  187.                                     ; This is based on the rates defined in <datadir>\CurrencyRates.txt
  188.                                     ; You should edit this file with the current currency rates.
  189.  
  190.     ShowUniqueEvaluation := 1       ; Display reminder when a unique is valuable.
  191.                                     ; This is based on <datadir>\ValuableUniques.txt
  192.                                     ; You can edit this file to suit your own needs.
  193.  
  194.     ShowGemEvaluation := 1          ; Display reminder when a gem is valuable and/or drop only.
  195.                                     ; This is based on <datadir>\ValuableGems.txt and <datadir>\DropOnlyGems.txt
  196.                                     ; You can edit these files to suit your own needs.
  197.  
  198.     GemQualityValueThreshold := 10  ; If the gem's added quality exceeds this value, consider it valuable regardless of which gem it is.
  199.  
  200.     MaxSpanStartingFromFirst := 1   ; When showing max possible, don't just show the highest possible affix bracket
  201.                                     ; but construct a pseudo range which spans the lower bound of the lowest possible
  202.                                     ; bracket to the upper bound of the highest possible one.
  203.                                     ;
  204.                                     ; This is usually what you want to see when evaluating an item's worth. The exception
  205.                                     ; being when you want to reroll an affix to the highest possible value within it's
  206.                                     ; current bracket - then you need to see the affix range that is actually on the item
  207.                                     ; right now.
  208.  
  209.     CompactDoubleRanges := 1        ; Show double ranges as "1-172" instead of "1-8 to 160-172"
  210.     CompactAffixTypes := 1          ; Use compact affix type designations: Suffix = S, Prefix = P, Comp. Suffix = CS, Comp. Prefix = CP
  211.  
  212.     MarkHighLinksAsValuable := 1    ; Mark rares or uniques with 5L or 6L as valuable.
  213.  
  214.     MirrorAffixLines := 1           ; Show a copy of the affix line in question when showing affix details.
  215.                                     ;
  216.                                     ; For example, would display "Prefix, 5-250" instead of "+246 to Accuracy Rating, Prefix, 5-250".
  217.                                     ; Since the affixes are processed in order one can attribute which is which to the ordering of
  218.                                     ; the lines in the tooltip to the item data in game.
  219.  
  220.     MirrorLineFieldWidth := 18      ; Mirrored affix line width. Set to a number above 0 to truncate (or pad) to this many characters.
  221.                                     ; Appends AffixDetailEllipsis when truncating.
  222.     ValueRangeFieldWidth := 7       ; Width of field that displays the affix' value range(s). Set to a number larger than 0 to truncate (or pad) to this many characters.
  223.                                     ;
  224.                                     ; Keep in mind that there are sometimes double ranges to be displayed. Like for example on an axe, implicit physical damage might
  225.                                     ; have a lower bound range and a upper bound range. In this case the lower bound range can have at most a 3 digit minimum value,
  226.                                     ; and at most a 3 digit maximum value. To then display just the lower bound (which constitutes one value range field), you would need
  227.                                     ; at least 7 characters (ex: 132-179). To complete the example here is how it would look like with 2 fields (lower and upper bound)
  228.                                     ; 132-179 168-189. Note that you don't need to set 15 as option value to display both fields correctly. As the name implies the option
  229.                                     ; is per field, so a value of 8 can display two 8 character wide fields correctly.
  230.  
  231.     AffixDetailDelimiter := " "     ; Field delimiter for affix detail lines. This is put between value range fields. If this value were set to a comma, the above
  232.                                     ; double range example would become 132-179,168-189.
  233.  
  234.     AffixDetailEllipsis := "…"      ; If the MirrorLineFieldWidth is set to a value that is smaller than the actual length of the affix line text
  235.                                     ; the affix line will be cut off and this text will be appended at the end to indicate tha the line was truncated.
  236.                                     ;
  237.                                     ; Usually this is set to the ASCII or Unicode value of the three dot ellipsis (alt code: 0133).
  238.                                     ; Note that the correct display of text characters outside the ASCII standard depend on the file encoding and the
  239.                                     ; AHK version used. For best results, save this file as ANSI encoding which can be read and displayed correctly by
  240.                                     ; either ANSI based AutoHotkey or Unicode based AutoHotkey.
  241.                                     ;
  242.                                     ; Example: assume the affix line to be mirrored is '+#% increased Spell Damage'.
  243.                                     ; If the MirrorLineFieldWidth is set to 18, this field would be shown as '+#% increased Spel…'
  244.  
  245.     PutResultsOnClipboard := 0      ; Put result text on clipboard (overwriting the textual representation the game put there to begin with)
  246.  
  247.     ; Pixels mouse must move to auto-dismiss tooltip
  248.     MouseMoveThreshold := 40
  249.  
  250.     ; Set this to 1 if you want to have the tooltip disappear after the time frame set below.
  251.     ; Otherwise you will have to move the mouse by 5 pixels for the tip to disappear.
  252.     UseTooltipTimeout := 0
  253.  
  254.     ;How many ticks to wait before removing tooltip. 1 tick = 100ms. Example, 50 ticks = 5secends, 75 Ticks = 7.5Secends
  255.     ToolTipTimeoutTicks := 150
  256.  
  257.     ; Font size for the tooltip, leave empty for default
  258.     FontSize := 11
  259.  
  260.     ; Displays the tooltip in virtual screen space at fixed coordinates.
  261.     ; Virtual screen space means the complete desktop frame, including any secondary monitors.
  262.     DisplayToolTipAtFixedCoords := 0
  263.    
  264.     ; Coordinates relative to top left corner, increasing by going down and to the right.
  265.     ; Only used if DisplayToolTipAtFixedCoords is 1.
  266.     ScreenOffsetX := 0
  267.     ScreenOffsetY := 0
  268.  
  269.     ScanUI()
  270.     {
  271.         this.OnlyActiveIfPOEIsFront := GuiGet("OnlyActiveIfPOEIsFront")
  272.         this.ShowItemLevel := GuiGet("ShowItemLevel")
  273.         this.ShowMaxSockets := GuiGet("ShowMaxSockets")
  274.         this.ShowDamageCalculations := GuiGet("ShowDamageCalculations")
  275.         this.ShowAffixTotals := GuiGet("ShowAffixTotals")
  276.         this.ShowAffixDetails := GuiGet("ShowAffixDetails")
  277.         this.ShowAffixLevel := GuiGet("ShowAffixLevel")
  278.         this.ShowAffixBracket := GuiGet("ShowAffixBracket")
  279.         this.ShowAffixMaxPossible := GuiGet("ShowAffixMaxPossible")
  280.         this.ShowAffixBracketTier := GuiGet("ShowAffixBracketTier")
  281.         this.ShowAffixBracketTierTotal := GuiGet("ShowAffixBracketTierTotal")
  282.         this.TierRelativeToItemLevel := GuiGet("TierRelativeToItemLevel")
  283.         this.ShowCurrencyValueInChaos := GuiGet("ShowCurrencyValueInChaos")
  284.         this.DisplayToolTipAtFixedCoords := GuiGet("DisplayToolTipAtFixedCoords")
  285.         this.ScreenOffsetX := GuiGet("ScreenOffsetX")
  286.         this.ScreenOffsetY := GuiGet("ScreenOffsetY")
  287.         this.ShowUniqueEvaluation := GuiGet("ShowUniqueEvaluation")
  288.         this.ShowGemEvaluation := GuiGet("ShowGemEvaluation")
  289.         this.GemQualityValueThreshold := GuiGet("GemQualityValueThreshold")
  290.         this.MaxSpanStartingFromFirst := GuiGet("MaxSpanStartingFromFirst")
  291.         this.CompactDoubleRanges := GuiGet("CompactDoubleRanges")
  292.         this.CompactAffixTypes := GuiGet("CompactAffixTypes")
  293.         this.MarkHighLinksAsValuable := GuiGet("MarkHighLinksAsValuable")
  294.         this.MirrorAffixLines := GuiGet("MirrorAffixLines")
  295.         this.MirrorLineFieldWidth := GuiGet("MirrorLineFieldWidth")
  296.         this.ValueRangeFieldWidth := GuiGet("ValueRangeFieldWidth")
  297.         this.AffixDetailDelimiter := GuiGet("AffixDetailDelimiter")
  298.         this.AffixDetailEllipsis := GuiGet("AffixDetailEllipsis")
  299.         this.PutResultsOnClipboard := GuiGet("PutResultsOnClipboard")  
  300.         this.MouseMoveThreshold := GuiGet("MouseMoveThreshold")
  301.         this.UseTooltipTimeout := GuiGet("UseTooltipTimeout")
  302.         this.ToolTipTimeoutTicks := GuiGet("ToolTipTimeoutTicks")
  303.         this.FontSize := GuiGet("FontSize")
  304.     }
  305. }
  306. Opts := new UserOptions()
  307.  
  308. class Fonts {
  309.    
  310.     Init(FontSizeFixed, FontSizeUI)
  311.     {
  312.         this.FontSizeFixed := FontSizeFixed
  313.         this.FontSizeUI := FontSizeUI
  314.         this.FixedFont := this.CreateFixedFont(FontSizeFixed)
  315.         this.UIFont := this.CreateUIFont(FontSizeUI)
  316.     }
  317.    
  318.     CreateFixedFont(FontSize_)
  319.     {
  320.         Options :=
  321.         If (!(FontSize_ == ""))
  322.         {
  323.             Options = s%FontSize_%
  324.         }
  325.         Gui Font, %Options%, Courier New
  326.         Gui Font, %Options%, Consolas
  327.         Gui Add, Text, HwndHidden,
  328.         SendMessage, 0x31,,,, ahk_id %Hidden%
  329.         return ErrorLevel
  330.     }
  331.  
  332.     CreateUIFont(FontSize_)
  333.     {
  334.         Options :=
  335.         If (!(FontSize_ == ""))
  336.         {
  337.             Options = s%FontSize_%
  338.         }
  339.         Gui Font, %Options%, Tahoma
  340.         Gui Font, %Options%, Segoe UI
  341.         Gui Add, Text, HwndHidden,
  342.         SendMessage, 0x31,,,, ahk_id %Hidden%
  343.         return ErrorLevel
  344.     }
  345.    
  346.     Set(NewFont)
  347.     {
  348.         AhkExe := GetAhkExeFilename()
  349.         SendMessage, 0x30, NewFont, 1,, ahk_class tooltips_class32 ahk_exe %AhkExe%
  350.         ; Development versions of AHK
  351.         SendMessage, 0x30, NewFont, 1,, ahk_class tooltips_class32 ahk_exe AutoHotkeyA32.exe
  352.         SendMessage, 0x30, NewFont, 1,, ahk_class tooltips_class32 ahk_exe AutoHotkeyU32.exe
  353.         SendMessage, 0x30, NewFont, 1,, ahk_class tooltips_class32 ahk_exe AutoHotkeyU64.exe
  354.     }
  355.    
  356.     SetFixedFont(FontSize_=-1)
  357.     {
  358.         If (FontSize_ == -1)
  359.         {
  360.             FontSize_ := this.FontSizeFixed
  361.         }
  362.         Else
  363.         {
  364.             this.FontSizeFixed := FontSize_
  365.             this.FixedFont := this.CreateFixedFont(FontSize_)
  366.         }
  367.         this.Set(this.FixedFont)
  368.     }
  369.  
  370.     SetUIFont(FontSize_=-1)
  371.     {
  372.         If (FontSize_ == -1)
  373.         {
  374.             FontSize_ := this.FontSizeUI
  375.         }
  376.         Else
  377.         {
  378.             this.FontSizeUI := FontSize_
  379.             this.UIFont := this.CreateUIFont(FontSize_)
  380.         }
  381.         this.Set(this.UIFont)
  382.     }
  383.    
  384.     GetFixedFont()
  385.     {
  386.         return this.FixedFont
  387.     }
  388.    
  389.     GetUIFont()
  390.     {
  391.         return this.UIFont
  392.     }
  393. }
  394.  
  395. class ItemData_ {
  396.  
  397.     Links := ""
  398.     Stats := ""
  399.     NamePlate := ""
  400.     Affixes := ""
  401.     FullText := ""
  402.     IndexAffixes := -1
  403.     IndexLast := -1
  404.     PartsLast := ""
  405.     Rarity := ""
  406.     Parts := []
  407.    
  408.     ClearParts()
  409.     {
  410.         Loop, % this.Parts.MaxIndex()
  411.         {
  412.             this.Parts.Remove(this.Parts.MaxIndex())
  413.         }
  414.     }
  415. }
  416. ItemData := new ItemData_()
  417.  
  418. class Item {
  419.     Name := ""
  420.     TypeName := ""
  421.     Quality := ""
  422.     BaseLevel := ""
  423.     RarityLevel := ""
  424.     BaseType := ""
  425.     SubType := ""
  426.     GripType := ""
  427.     Level := ""
  428.     MaxSockets := ""
  429.     IsUnidentified := ""
  430.     IsCorrupted := ""
  431.     IsGem := ""
  432.     IsCurrency := ""
  433.     IsUnique := ""
  434.     IsRare := ""
  435.     IsBow := ""
  436.     IsFlask := ""
  437.     IsBelt := ""
  438.     IsRing := ""
  439.     IsUnsetRing := ""
  440.     IsAmulet := ""
  441.     IsSingleSocket := ""
  442.     IsFourSocket := ""
  443.     IsThreeSocket := ""
  444.     IsQuiver := ""
  445.     IsWeapon := ""
  446.     IsMap := ""
  447.     IsMirrored := ""
  448.     HasEffect := ""
  449. }
  450. Item := new Item()
  451.  
  452. class AffixTotals_ {
  453.  
  454.     NumPrefixes := 0
  455.     NumSuffixes := 0
  456.     NumTotals := 0
  457.    
  458.     Reset()
  459.     {
  460.         this.NumPrefixes := 0
  461.         this.NumSuffixes := 0
  462.         this.NumTotals := 0
  463.     }
  464. }
  465. AffixTotals := new AffixTotals_()
  466.  
  467. class AffixLines_ {
  468.    
  469.     __New()
  470.     {
  471.         this.Length := 0
  472.     }
  473.    
  474.     ; Sets fields to empty string
  475.     Clear(Index)
  476.     {
  477.         this[Index] := ""
  478.     }
  479.    
  480.     ClearAll()
  481.     {
  482.         Loop, % this.MaxIndex()
  483.         {
  484.             this.Clear(A_Index)
  485.         }
  486.     }
  487.    
  488.     ; Actually removes fields
  489.     Reset()
  490.     {
  491.         Loop, % this.MaxIndex()
  492.         {
  493.             this.Remove(this.MaxIndex())
  494.         }
  495.         this.Length := 0
  496.     }
  497.    
  498.     Set(Index, Contents)
  499.     {
  500.         this[Index] := Contents
  501.         this.Length := this.MaxIndex()
  502.     }
  503. }
  504. AffixLines := new AffixLines_()
  505.  
  506. IfNotExist, %A_ScriptDir%\config.ini
  507. {
  508.     IfNotExist, %A_ScriptDir%\data\defaults.ini
  509.     {
  510.         CreateDefaultConfig()
  511.     }
  512.     CopyDefaultConfig()
  513. }
  514.  
  515. ; Windows system tray icon
  516. ; possible values: poe.ico, poe-bw.ico, poe-web.ico, info.ico
  517. ; set before creating the settings UI so it gets used for the settigns dialog as well
  518. Menu, Tray, Icon, %A_ScriptDir%\data\poe-bw.ico
  519.  
  520. ReadConfig()
  521. Sleep, 100
  522. CreateSettingsUI()
  523.  
  524. Menu, TextFiles, Add, Valuable Uniques, EditValuableUniques
  525. Menu, TextFiles, Add, Valuable Gems, EditValuableGems
  526. Menu, TextFiles, Add, Dropy Only Gems, EditDropOnlyGems
  527. Menu, TextFiles, Add, Currency Rates, EditCurrencyRates
  528.  
  529.  
  530. ; Menu tooltip
  531. RelVer := Globals.Get("ReleaseVersion")
  532. Menu, Tray, Tip, Path of Exile Item Info %RelVer%
  533.  
  534. Menu, Tray, NoStandard
  535. Menu, Tray, Add, About..., MenuTray_About
  536. Menu, Tray, Add, PoE Item Info Settings, ShowSettingsUI
  537. Menu, Tray, Add ; Separator
  538. Menu, Tray, Add, Edit, :TextFiles
  539. Menu, Tray, Add ; Separator
  540. Menu, Tray, Standard
  541. Menu, Tray, Default, PoE Item Info Settings
  542.  
  543.  
  544. IfNotExist, %A_ScriptDir%\data
  545. {
  546.     MsgBox, 16, % Msg.DataDirNotFound
  547.     exit
  548. }
  549.  
  550. #Include %A_ScriptDir%\data\MapList.txt
  551.  
  552. Fonts.Init(Opts.FontSize, 9)
  553.  
  554. GetAhkExeFilename(Default_="AutoHotkey.exe")
  555. {
  556.     AhkExeFilename := Default_
  557.     If (A_AhkPath)
  558.     {
  559.         StringSplit, AhkPathParts, A_AhkPath, \
  560.         Loop, % AhkPathParts0
  561.         {
  562.             IfInString, AhkPathParts%A_Index%, .exe
  563.             {
  564.                 AhkExeFilename := AhkPathParts%A_Index%
  565.                 Break
  566.             }
  567.         }
  568.     }
  569.     return AhkExeFilename
  570. }
  571.  
  572. OpenCreateDataTextFile(Filename)
  573. {
  574.     Filepath := A_ScriptDir . "\data\" . Filename
  575.     IfExist, % Filepath
  576.     {
  577.         Run, % Filepath
  578.     }
  579.     Else
  580.     {
  581.        
  582.         File := FileOpen(Filepath, "w")
  583.         if !IsObject(File)
  584.         {
  585.             MsgBox, 16, Can't create %A_ScriptDir%\data\ValuableUniques.txt
  586.             return
  587.         }
  588.         File.Close()
  589.         Run, % Filepath
  590.     }
  591.     return
  592.  
  593.     Run, %A_ScriptDir%\data\%Filename%
  594.     return
  595. }
  596.  
  597.  
  598. ParseElementalDamage(String, DmgType, ByRef DmgLo, ByRef DmgHi)
  599. {
  600.     IfInString, String, %DmgType% Damage
  601.     {
  602.         IfInString, String, Converted to or IfInString, String, taken as
  603.         {
  604.             return
  605.         }
  606.         IfNotInString, String, increased
  607.         {
  608.             StringSplit, Arr, String, %A_Space%
  609.             StringSplit, Arr, Arr2, -
  610.             DmgLo := Arr1
  611.             DmgHi := Arr2
  612.         }
  613.     }
  614. }
  615.  
  616. ; Function that checks item type name against entries
  617. ; from ItemList.txt to get the item's base level
  618. ; Added by kongyuyu, changed by hazydoc
  619. CheckBaseLevel(ItemTypeName)
  620. {
  621.     ItemListArray = 0
  622.     Loop, Read, %A_ScriptDir%\data\ItemList.txt
  623.     {  
  624.         ; This loop retrieves each line from the file, one at a time.
  625.         ItemListArray += 1  ; Keep track of how many items are in the array.
  626.         StringSplit, NameLevel, A_LoopReadLine, |,
  627.         Array%ItemListArray%1 := NameLevel1  ; Store this line in the next array element.
  628.         Array%ItemListArray%2 := NameLevel2
  629.     }
  630.  
  631.     Loop %ItemListArray% {
  632.         element := Array%A_Index%1
  633.         If(ItemTypeName == element)
  634.         {
  635.             BaseLevel := Array%A_Index%2
  636.             Break
  637.         }
  638.     }
  639.     return BaseLevel
  640. }
  641.  
  642. CheckRarityLevel(RarityString)
  643. {
  644.     IfInString, RarityString, Normal
  645.         return 1
  646.     IfInString, RarityString, Magic
  647.         return 2
  648.     IfInString, RarityString, Rare
  649.         return 3
  650.     IfInString, RarityString, Unique
  651.         return 4
  652.     return 0 ; unknown rarity. shouldn't happen!
  653. }
  654.  
  655. ParseItemType(ItemDataStats, ItemDataNamePlate, ByRef BaseType, ByRef SubType, ByRef GripType)
  656. {
  657.     ; Grip type only matters for weapons at this point. For all others it will be 'None'.
  658.     GripType = None
  659.  
  660.     ; Check stats section first as weapons usually have their sub type as first line
  661.     Loop, Parse, ItemDataStats, `n, `r
  662.     {
  663.         IfInString, A_LoopField, One Handed Axe
  664.         {
  665.             BaseType = Weapon
  666.             SubType = Axe
  667.             GripType = 1H
  668.             return
  669.         }
  670.         IfInString, A_LoopField, Two Handed Axe
  671.         {
  672.             BaseType = Weapon
  673.             SubType = Axe
  674.             GripType = 2H
  675.             return
  676.         }
  677.         IfInString, A_LoopField, One Handed Mace
  678.         {
  679.             BaseType = Weapon
  680.             SubType = Mace
  681.             GripType = 1H
  682.             return
  683.         }
  684.         IfInString, A_LoopField, Two Handed Mace
  685.         {
  686.             BaseType = Weapon
  687.             SubType = Mace
  688.             GripType = 2H
  689.             return
  690.         }
  691.         IfInString, A_LoopField, Sceptre
  692.         {
  693.             BaseType = Weapon
  694.             SubType = Sceptre
  695.             GripType = 1H
  696.             return
  697.         }
  698.         IfInString, A_LoopField, Staff
  699.         {
  700.             BaseType = Weapon
  701.             SubType = Staff
  702.             GripType = 2H
  703.             return
  704.         }
  705.         IfInString, A_LoopField, One Handed Sword
  706.         {
  707.             BaseType = Weapon
  708.             SubType = Sword
  709.             GripType = 1H
  710.             return
  711.         }
  712.         IfInString, A_LoopField, Two Handed Sword
  713.         {
  714.             BaseType = Weapon
  715.             SubType = Sword
  716.             GripType = 2H
  717.             return
  718.         }
  719.         IfInString, A_LoopField, Dagger
  720.         {
  721.             BaseType = Weapon
  722.             SubType = Dagger
  723.             GripType = 1H
  724.             return
  725.         }
  726.         IfInString, A_LoopField, Claw
  727.         {
  728.             BaseType = Weapon
  729.             SubType = Claw
  730.             GripType = 1H
  731.             return
  732.         }
  733.         IfInString, A_LoopField, Bow
  734.         {
  735.             ; Not really sure if I should classify bow as 2H (because that would make sense)
  736.             ; but you can equip a quiver in 2nd hand slot, so it could be 1H?
  737.             BaseType = Weapon
  738.             SubType = Bow
  739.             GripType = 1H
  740.             return
  741.         }
  742.         IfInString, A_LoopField, Wand
  743.         {
  744.             BaseType = Weapon
  745.             SubType = Wand
  746.             GripType = 1H
  747.             return
  748.         }
  749.     }
  750.  
  751.     ; Check name plate section
  752.     Loop, Parse, ItemDataNamePlate, `n, `r
  753.     {
  754.         ; a few cases that cause incorrect id later
  755.         ; and thus should come first
  756.         ; Note: still need to work on proper id for
  757.         ; all armour types.
  758.         IfInString, A_LoopField, Ringmail Gloves
  759.         {
  760.             BaseType = Armour
  761.             SubType = Gloves
  762.             return
  763.         }
  764.         IfInString, A_LoopField, Ringmail Boots
  765.         {
  766.             BaseType = Armour
  767.             SubType = Gloves
  768.             return
  769.         }
  770.         If (RegExMatch(A_LoopField, "Ringmail$"))
  771.         {
  772.             BaseType = Armour
  773.             SubType = BodyArmour
  774.             return
  775.         }
  776.         IfInString, A_LoopField, Mantle
  777.         {
  778.             BaseType = Armour
  779.             SubType = BodyArmour
  780.             return
  781.         }
  782.         IfInString, A_LoopField, Shell
  783.         {
  784.             BaseType = Armour
  785.             SubType = BodyArmour
  786.             return
  787.         }
  788.  
  789.         ; Belts, Amulets, Rings, Quivers, Flasks
  790.         IfInString, A_LoopField, Rustic Sash
  791.         {
  792.             BaseType = Item
  793.             SubType = Belt
  794.             return
  795.         }
  796.         IfInString, A_LoopField, Belt
  797.         {
  798.             BaseType = Item
  799.             SubType = Belt
  800.             return
  801.         }
  802.         IfInString, A_LoopField, Amulet
  803.         {
  804.             BaseType = Item
  805.             SubType = Amulet
  806.             return
  807.         }
  808.         If(RegExMatch(A_LoopField, "\bRing\b"))
  809.         {
  810.             BaseType = Item
  811.             SubType = Ring
  812.             return
  813.         }
  814.         IfInString, A_LoopField, Quiver
  815.         {
  816.             BaseType = Item
  817.             SubType = Quiver
  818.             return
  819.         }
  820.         IfInString, A_LoopField, Flask
  821.         {
  822.             BaseType = Item
  823.             SubType = Flask
  824.             return
  825.         }
  826.         IfInString, A_LoopField, %A_Space%Map
  827.         {
  828.             Global matchList
  829.             BaseType = Map      
  830.             Loop % matchList.MaxIndex()
  831.             {
  832.                 Match := matchList[A_Index]
  833.                 IfInString, A_LoopField, %Match%
  834.                 {
  835.                     SubType = %Match%
  836.                     return
  837.                 }
  838.             }
  839.            
  840.             SubType = Unknown%A_Space%Map
  841.             return
  842.         }
  843.         ; Dry Peninsula fix
  844.         IfInString, A_LoopField, Dry%A_Space%Peninsula
  845.         {
  846.             BaseType = Map
  847.             SubType = Dry%A_Space%Peninsula
  848.             return
  849.         }      
  850.  
  851.         ; Shields
  852.         IfInString, A_LoopField, Shield
  853.         {
  854.             BaseType = Armour
  855.             SubType = Shield
  856.             return
  857.         }
  858.         IfInString, A_LoopField, Buckler
  859.         {
  860.             BaseType = Armour
  861.             SubType = Shield
  862.             return
  863.         }
  864.         IfInString, A_LoopField, Bundle
  865.         {
  866.             BaseType = Armour
  867.             SubType = Shield
  868.             return
  869.         }
  870.         IfInString, A_LoopField, Gloves
  871.         {
  872.             BaseType = Armour
  873.             SubType = Gloves
  874.             return
  875.         }
  876.         IfInString, A_LoopField, Mitts
  877.         {
  878.             BaseType = Armour
  879.             SubType = Gloves
  880.             return
  881.         }
  882.         IfInString, A_LoopField, Gauntlets
  883.         {
  884.             BaseType = Armour
  885.             SubType = Gloves
  886.             return
  887.         }
  888.  
  889.         ; Helmets
  890.         IfInString, A_LoopField, Helmet
  891.         {
  892.             BaseType = Armour
  893.             SubType = Helmet
  894.             return
  895.         }
  896.         IfInString, A_LoopField, Helm
  897.         {
  898.             BaseType = Armour
  899.             SubType = Helmet
  900.             return
  901.         }
  902.         If (InStr(A_LoopField, "Hat") and (Not InStr(A_LoopField, "Hate")))
  903.         {
  904.             BaseType = Armour
  905.             SubType = Helmet
  906.             return
  907.         }
  908.         IfInString, A_LoopField, Mask
  909.         {
  910.             BaseType = Armour
  911.             SubType = Helmet
  912.             return
  913.         }
  914.         IfInString, A_LoopField, Hood
  915.         {
  916.             BaseType = Armour
  917.             SubType = Helmet
  918.             return
  919.         }
  920.         IfInString, A_LoopField, Ursine Pelt
  921.         {
  922.             BaseType = Armour
  923.             SubType = Helmet
  924.             return
  925.         }
  926.         IfInString, A_LoopField, Lion Pelt
  927.         {
  928.             BaseType = Armour
  929.             SubType = Helmet
  930.             return
  931.         }
  932.         IfInString, A_LoopField, Circlet
  933.         {
  934.             BaseType = Armour
  935.             SubType = Helmet
  936.             return
  937.         }
  938.         IfInString, A_LoopField, Sallet
  939.         {
  940.             BaseType = Armour
  941.             SubType = Helmet
  942.             return
  943.         }
  944.         IfInString, A_LoopField, Burgonet
  945.         {
  946.             BaseType = Armour
  947.             SubType = Helmet
  948.             return
  949.         }
  950.         IfInString, A_LoopField, Bascinet
  951.         {
  952.             BaseType = Armour
  953.             SubType = Helmet
  954.             return
  955.         }
  956.         IfInString, A_LoopField, Crown
  957.         {
  958.             BaseType = Armour
  959.             SubType = Helmet
  960.             return
  961.         }
  962.         IfInString, A_LoopField, Cage
  963.         {
  964.             BaseType = Armour
  965.             SubType = Helmet
  966.             return
  967.         }
  968.         IfInString, A_LoopField, Tricorne
  969.         {
  970.             BaseType = Armour
  971.             SubType = Helmet
  972.             return
  973.         }
  974.        
  975.         ; Boots
  976.         IfInString, A_LoopField, Boots
  977.         {
  978.             BaseType = Armour
  979.             SubType = Boots
  980.             return
  981.         }
  982.         IfInString, A_LoopField, Greaves
  983.         {
  984.             BaseType = Armour
  985.             SubType = Boots
  986.             return
  987.         }  
  988.         IfInString, A_LoopField, Slippers
  989.         {
  990.             BaseType = Armour
  991.             SubType = Boots
  992.             return
  993.         }                  
  994.     }
  995.  
  996.     ; TODO: need a reliable way to determine sub type for armour
  997.     ; right now it's just determine anything else first if it's
  998.     ; not that, it's armour.
  999.     BaseType = Armour
  1000.     SubType = Armour
  1001. }
  1002.  
  1003. GetClipboardContents(DropNewlines=False)
  1004. {
  1005.     Result =
  1006.     If Not DropNewlines
  1007.     {
  1008.         Loop, Parse, Clipboard, `n, `r
  1009.         {
  1010.             Result := Result . A_LoopField . "`r`n"
  1011.         }
  1012.     }
  1013.     Else
  1014.     {  
  1015.         Loop, Parse, Clipboard, `n, `r
  1016.         {
  1017.             Result := Result . A_LoopField
  1018.         }
  1019.     }
  1020.     return Result
  1021. }
  1022.  
  1023. SetClipboardContents(String)
  1024. {
  1025.     Clipboard := String
  1026. }
  1027.  
  1028. ; Splits StrInput on StrDelimiter. Returns an object that has a 'length' field
  1029. ; containing the number of parts and 0 .. (length) fields containing the substrings.
  1030. ; Example: if parts is the object returned by this function, then
  1031. ;   'parts.length' gives the number of parts
  1032. ;   'parts[1]' gives the first part (if there is one)
  1033. ; Note: if StrDelimiter is not present in StrInput, length == 1 and parts[1] == StrInput
  1034. ; Note2: as per AHK docs, parts.(Min|Max)Index() also work of course.
  1035. SplitString(StrInput, StrDelimiter)
  1036. {
  1037.     TempDelim := "``"
  1038.     Chunks := Object()
  1039.     StringReplace, TempResult, StrInput, %StrDelimiter%, %TempDelim%, All
  1040.     StringSplit, Parts, TempResult, %TempDelim%
  1041.     Chunks["length"] := Parts0
  1042.     Loop, %Parts0%
  1043.     {
  1044.         Chunks[A_Index] := Parts%A_Index%
  1045.     }
  1046.     return Chunks
  1047. }
  1048.  
  1049. ; TODO: LookupAffixBracket and LookupAffixData contain a lot of duplicate code.
  1050.  
  1051. ; Look up just the most applicable bracket for an affix.
  1052. ; Most applicable means Value is between bounds of bracket range or
  1053. ; highest entry possible given the item level.
  1054. ;
  1055. ; Returns "#-#" format range
  1056. ;
  1057. ; If Value is unspecified ("") return the max possible
  1058. ; bracket based on item level
  1059. LookupAffixBracket(Filename, ItemLevel, Value="", ByRef BracketLevel="", ByRef BracketIndex=0)
  1060. {
  1061.     AffixLevel := 0
  1062.     AffixDataIndex := 0
  1063.     If (Not Value == "")
  1064.     {
  1065.         ValueLo := Value             ; Value from ingame tooltip
  1066.         ValueHi := Value             ; For single values (which most of them are) ValueLo == ValueHi
  1067.         ParseRange(Value, ValueHi, ValueLo)
  1068.     }
  1069.     LookupIsDoubleRange := False ; For affixes like "Adds +# ... Damage" which have a lower and an upper bound range
  1070.     BracketRange := "n/a"
  1071.     Loop, Read, %A_ScriptDir%\%Filename%
  1072.     {  
  1073.         AffixDataIndex += 1
  1074.         StringSplit, AffixDataParts, A_LoopReadLine, |,
  1075.         RangeLevel := AffixDataParts1
  1076.         RangeValues := AffixDataParts2
  1077.         If (RangeLevel > ItemLevel)
  1078.         {
  1079.             AffixDataIndex -= 1 ; Since we added 1 above, before we noticed range level is above item level
  1080.             Break
  1081.         }
  1082.         IfInString, RangeValues, `,
  1083.         {
  1084.             LookupIsDoubleRange := True
  1085.         }
  1086.         If (LookupIsDoubleRange)
  1087.         {
  1088.             ; Example lines from txt file database for double range lookups:
  1089.             ;  3|1,14-15
  1090.             ; 13|1-3,35-37
  1091.             StringSplit, DoubleRangeParts, RangeValues, `,
  1092.             LB := DoubleRangeParts%DoubleRangeParts%1
  1093.             UB := DoubleRangeParts%DoubleRangeParts%2
  1094.             ; Default case: lower bound is single value: #
  1095.             ; see level 3 case in example lines above
  1096.             LBMin := LB
  1097.             LBMax := LB
  1098.             UBMin := UB
  1099.             UBMax := UB
  1100.             IfInString, LB, -
  1101.             {
  1102.                 ; Lower bound is a range: #-#
  1103.                 ParseRange(LB, LBMax, LBMin)
  1104.             }
  1105.             IfInString, UB, -
  1106.             {
  1107.                 ParseRange(UB, UBMax, UBMin)
  1108.             }
  1109.             LBPart = %LBMin%
  1110.             UBPart = %UBMax%
  1111.             ; Record bracket range if it is within bounds of the text file entry
  1112.             If (Value == "" or (((ValueLo >= LBMin) and (ValueLo <= LBMax)) and ((ValueHi >= UBMin) and (ValueHi <= UBMax))))
  1113.             {
  1114.                 BracketRange = %LBPart%-%UBPart%
  1115.                 AffixLevel = %RangeLevel%
  1116.             }
  1117.         }
  1118.         Else
  1119.         {
  1120.             ParseRange(RangeValues, HiVal, LoVal)
  1121.             ; Record bracket range if it is within bounds of the text file entry
  1122.             If (Value == "" or ((ValueLo >= LoVal) and (ValueHi <= HiVal)))
  1123.             {
  1124.                 BracketRange = %LoVal%-%HiVal%
  1125.                 AffixLevel = %RangeLevel%
  1126.             }
  1127.         }
  1128.         If (Value == "")
  1129.         {
  1130.             AffixLevel = %RangeLevel%
  1131.         }
  1132.     }
  1133.     BracketIndex := AffixDataIndex
  1134.     BracketLevel := AffixLevel
  1135.     return BracketRange
  1136. }
  1137.  
  1138. ; Look up complete data for an affix. Depending on settings flags
  1139. ; this may include many things, and will return a string used for
  1140. ; end user display rather than further calculations.
  1141. ; Use LookupAffixBracket if you need a range format to do calculations with.
  1142. LookupAffixData(Filename, ItemLevel, Value, ByRef BracketLevel="", ByRef Tier=0)
  1143. {
  1144.     Global Opts
  1145.    
  1146.     AffixLevel := 0
  1147.     AffixDataIndex := 0
  1148.     ValueLo := Value             ; Value from ingame tooltip
  1149.     ValueHi := Value             ; For single values (which most of them are) ValueLo == ValueHi
  1150.     ValueIsMinMax := False       ; Treat Value as min/max units (#-#) or as single unit (#)
  1151.     LookupIsDoubleRange := False ; For affixes like "Adds +# ... Damage" which have a lower and an upper bound range
  1152.     FirstRangeValues =
  1153.     BracketRange := "n/a"
  1154.     MaxRange =
  1155.     FinalRange =
  1156.     MaxLevel := 1
  1157.     RangeLevel := 1
  1158.     Tier := 0
  1159.     MaxTier := 0
  1160.     IfInString, Value, -
  1161.     {
  1162.         ParseRange(Value, ValueHi, ValueLo)
  1163.         ValueIsMinMax := True
  1164.     }
  1165.     ; TODO refactor pre-pass into its own method
  1166.     ; Pre-pass to determine max tier
  1167.     Loop, Read, %A_ScriptDir%\%Filename%
  1168.     {  
  1169.         StringSplit, AffixDataParts, A_LoopReadLine, |,
  1170.         RangeLevel := AffixDataParts1
  1171.         If (Globals.Get("TierRelativeToItemLevelOverride", Opts.TierRelativeToItemLevel) and (RangeLevel > ItemLevel))
  1172.         {
  1173.             Break
  1174.         }
  1175.         ; Yes, this is correct incrementing MaxTier here and not before the break!
  1176.         MaxTier += 1
  1177.     }
  1178.  
  1179.     Loop, Read, %A_ScriptDir%\%Filename%
  1180.     {  
  1181.         AffixDataIndex += 1
  1182.         StringSplit, AffixDataParts, A_LoopReadLine, |,
  1183.         RangeValues := AffixDataParts2
  1184.         RangeLevel := AffixDataParts1
  1185.         If (AffixDataIndex == 1)
  1186.         {
  1187.             FirstRangeValues := RangeValues
  1188.         }
  1189.         If (Globals.Get("TierRelativeToItemLevelOverride", Opts.TierRelativeToItemLevel) and (RangeLevel > ItemLevel))
  1190.         {
  1191.             Break
  1192.         }
  1193.         MaxLevel := RangeLevel
  1194.         IfInString, RangeValues, `,
  1195.         {
  1196.             LookupIsDoubleRange := True
  1197.         }
  1198.         If (LookupIsDoubleRange)
  1199.         {
  1200.             ; Variables for min/max double ranges, like in the "Adds +# ... Damage" case
  1201.             ;       Global LBMin     ; (L)ower (B)ound minium value
  1202.             ;       Global LBMax     ; (L)ower (B)ound maximum value
  1203.             ;       GLobal UBMin     ; (U)pper (B)ound minimum value
  1204.             ;       GLobal UBMax     ; (U)pper (B)ound maximum value
  1205.             ;       ; same, just for the first range's values
  1206.             ;       Global FRLBMin  
  1207.             ;       Global FRLBMax  
  1208.             ;       Global FRUBMin  
  1209.             ;       Global FRUBMax  
  1210.             ; Example lines from txt file database for double range lookups:
  1211.             ;  3|1,14-15
  1212.             ; 13|1-3,35-37
  1213.             StringSplit, DoubleRangeParts, RangeValues, `,
  1214.             LB := DoubleRangeParts%DoubleRangeParts%1
  1215.             UB := DoubleRangeParts%DoubleRangeParts%2
  1216.             ; Default case: lower bound is single value: #
  1217.             ; see level 3 case in example lines above
  1218.             LBMin := LB
  1219.             LBMax := LB
  1220.             UBMin := UB
  1221.             UBMax := UB
  1222.             IfInString, LB, -
  1223.             {
  1224.                 ; Lower bound is a range: #-#
  1225.                 ParseRange(LB, LBMax, LBMin)
  1226.             }
  1227.             IfInString, UB, -
  1228.             {
  1229.                 ParseRange(UB, UBMax, UBMin)
  1230.             }
  1231.             If (AffixDataIndex == 1)
  1232.             {
  1233.                 StringSplit, FirstDoubleRangeParts, FirstRangeValues, `,
  1234.                 FRLB := FirstDoubleRangeParts%FirstDoubleRangeParts%1
  1235.                 FRUB := FirstDoubleRangeParts%FirstDoubleRangeParts%2
  1236.                 ParseRange(FRUB, FRUBMax, FRUBMin)
  1237.                 ParseRange(FRLB, FRLBMax, FRLBMin)
  1238.             }
  1239.             If ((LBMin == LBMax) or Opts.CompactDoubleRanges)
  1240.             {
  1241.                 LBPart = %LBMin%
  1242.             }
  1243.             Else
  1244.             {
  1245.                 LBPart = %LBMin%-%LBMax%
  1246.             }
  1247.             If ((UBMin == UBMax) or Opts.CompactDoubleRanges)
  1248.             {
  1249.                 UBPart = %UBMax%
  1250.             }
  1251.             Else
  1252.             {
  1253.                 UBPart = %UBMin%-%UBMax%
  1254.             }
  1255.             If ((FRLBMin == FRLBMax) or Opts.CompactDoubleRanges)
  1256.             {
  1257.                 FRLBPart = %FRLBMin%
  1258.             }
  1259.             Else
  1260.             {
  1261.                 FRLBPart = %FRLBMin%-%FRLBMax%
  1262.             }
  1263.             If (Opts.CompactDoubleRanges)
  1264.             {
  1265.                 MiddlePart := "-"
  1266.             }
  1267.             Else
  1268.             {
  1269.                 MiddlePart := " to "
  1270.             }
  1271.             ; Record bracket range if it is withing bounds of the text file entry
  1272.             If (((ValueLo >= LBMin) and (ValueLo <= LBMax)) and ((ValueHi >= UBMin) and (ValueHi <= UBMax)))
  1273.             {
  1274.                 BracketRange = %LBPart%%MiddlePart%%UBPart%
  1275.                 AffixLevel = %MaxLevel%
  1276.                 Tier := ((MaxTier - AffixDataIndex) + 1)
  1277.                 If (Opts.ShowAffixBracketTierTotal)
  1278.                 {
  1279.                     Tier := Tier . "/" . MaxTier
  1280.                 }
  1281.             }
  1282.             ; Record max possible range regardless of within bounds
  1283.             If (Opts.MaxSpanStartingFromFirst)
  1284.             {
  1285.                 MaxRange = %FRLBPart%%MiddlePart%%UBPart%
  1286.             }
  1287.             Else
  1288.             {
  1289.                 MaxRange = %LBPart%%MiddlePart%%UBPart%
  1290.             }
  1291.         }
  1292.         Else
  1293.         {
  1294.             If (AffixDataIndex = 1)
  1295.             {
  1296.                 ParseRange(FirstRangeValues, FRHiVal, FRLoVal)
  1297.             }
  1298.             ParseRange(RangeValues, HiVal, LoVal)
  1299.             ; Record bracket range if it is within bounds of the text file entry
  1300.             If ((ValueLo >= LoVal) and (ValueHi <= HiVal))
  1301.             {
  1302.                 If (LoVal = HiVal)
  1303.                 {
  1304.                     BracketRange = %LoVal%
  1305.                 }
  1306.                 Else
  1307.                 {
  1308.                     BracketRange = %LoVal%-%HiVal%
  1309.                 }
  1310.                 AffixLevel = %MaxLevel%
  1311.                 Tier := ((MaxTier - AffixDataIndex) + 1)
  1312.                
  1313.                 If (Opts.ShowAffixBracketTierTotal)
  1314.                 {
  1315.                     Tier := Tier . "/" . MaxTier
  1316.                 }
  1317.             }
  1318.             ; Record max possible range regardless of within bounds
  1319.             If (Opts.MaxSpanStartingFromFirst)
  1320.             {
  1321.                 MaxRange = %FRLoVal%-%HiVal%
  1322.             }
  1323.             Else
  1324.             {
  1325.                 MaxRange = %LoVal%-%HiVal%
  1326.             }
  1327.         }
  1328.     }
  1329.     BracketLevel := AffixLevel
  1330.     FinalRange := AssembleValueRangeFields(BracketRange, BracketLevel, MaxRange, MaxLevel)
  1331.     return FinalRange
  1332. }
  1333.  
  1334. AssembleValueRangeFields(BracketRange, BracketLevel, MaxRange="", MaxLevel=0)
  1335. {
  1336.     Global Opts
  1337.    
  1338.     If (Opts.ShowAffixBracket)
  1339.     {
  1340.         FinalRange := BracketRange
  1341.         If (Opts.ValueRangeFieldWidth > 0)
  1342.         {
  1343.             FinalRange := StrPad(FinalRange, Opts.ValueRangeFieldWidth, "left")
  1344.         }
  1345.         If (Opts.ShowAffixLevel)
  1346.         {
  1347.             FinalRange := FinalRange . " " . StrPad("(" . BracketLevel . ")", 4, Side="left")
  1348.         }
  1349.         Else
  1350.         {
  1351.             FinalRange := FinalRange . Opts.AffixDetailDelimiter
  1352.         }
  1353.     }
  1354.     If (MaxRange and Opts.ShowAffixMaxPossible)
  1355.     {
  1356.         If (Opts.ValueRangeFieldWidth > 0)
  1357.         {
  1358.             MaxRange := StrPad(MaxRange, Opts.ValueRangeFieldWidth, "left")
  1359.         }
  1360.         FinalRange := FinalRange . MaxRange
  1361.         If (Opts.ShowAffixLevel)
  1362.         {
  1363.             FinalRange := FinalRange . " " . StrPad("(" . MaxLevel . ")", 4, Side="left")
  1364.         }
  1365.     }
  1366.     return FinalRange
  1367. }
  1368.  
  1369. ParseRarity(ItemData_NamePlate)
  1370. {
  1371.     Loop, Parse, ItemData_NamePlate, `n, `r
  1372.     {
  1373.         IfInString, A_LoopField, Rarity:
  1374.         {
  1375.             StringSplit, RarityParts, A_LoopField, %A_Space%
  1376.             Break
  1377.         }
  1378.     }
  1379.     return RarityParts%RarityParts%2
  1380. }
  1381.  
  1382. Assert(expr, msg)
  1383. {
  1384.     If (Not (expr))
  1385.     {
  1386.         MsgBox, 4112, Assertion Failure, %msg%
  1387.         ExitApp
  1388.     }
  1389. }
  1390.  
  1391. GetItemDataChunk(ItemDataText, MatchWord)
  1392. {
  1393.     Assert(StrLen(MatchWord) > 0, "GetItemDataChunk: parameter 'MatchWord' can't be empty")
  1394.    
  1395.     StringReplace, TempResult, ItemDataText, --------`r`n, ``, All  
  1396.     StringSplit, ItemDataChunks, TempResult, ``
  1397.     Loop, %ItemDataChunks0%
  1398.     {
  1399.         IfInString, ItemDataChunks%A_Index%, %MatchWord%
  1400.         {
  1401.             return ItemDataChunks%A_Index%
  1402.         }
  1403.     }
  1404. }
  1405.  
  1406. ParseQuality(ItemDataNamePlate)
  1407. {
  1408.     ItemQuality := 0
  1409.     Loop, Parse, ItemDataNamePlate, `n, `r
  1410.     {
  1411.         If (StrLen(A_LoopField) = 0)
  1412.         {
  1413.             Break
  1414.         }
  1415.         IfInString, A_LoopField, Unidentified
  1416.         {
  1417.             Break
  1418.         }
  1419.         IfInString, A_LoopField, Quality:
  1420.         {
  1421.             ItemQuality := RegExReplace(A_LoopField, "Quality: \+(\d+)% .*", "$1")
  1422.             Break
  1423.         }
  1424.     }
  1425.     return ItemQuality
  1426. }
  1427.  
  1428. ParseAugmentations(ItemDataChunk, ByRef AffixCSVList)
  1429. {
  1430.     CurAugment := ItemDataChunk
  1431.     Loop, Parse, ItemDataChunk, `n, `r
  1432.     {
  1433.         CurAugment := A_LoopField
  1434.         Globals.Set("CurAugment", A_LoopField)
  1435.         IfInString, A_LoopField, Requirements:
  1436.         {
  1437.             ; too far - Requirements: is already the next chunk
  1438.             Break
  1439.         }
  1440.         IfInString, A_LoopField, (augmented)
  1441.         {
  1442.             StringSplit, LineParts, A_LoopField, :
  1443.             AffixCSVList := AffixCSVList . "'"  . LineParts%LineParts%1 . "'"
  1444.             AffixCSVList := AffixCSVList . ", "
  1445.         }
  1446.     }
  1447.     AffixCSVList := SubStr(AffixCSVList, 1, -2)
  1448. }
  1449.  
  1450. ParseRequirements(ItemDataChunk, ByRef Level, ByRef Attributes, ByRef Values="")
  1451. {
  1452.     IfNotInString, ItemDataChunk, Requirements
  1453.     {
  1454.         return
  1455.     }
  1456.     Attr =
  1457.     AttrValues =
  1458.     Delim := ","
  1459.     DelimLen := StrLen(Delim)
  1460.     Loop, Parse, ItemDataChunk, `n, `r
  1461.     {    
  1462.         If StrLen(A_LoopField) = 0
  1463.         {
  1464.             Break ; Not interested in blank lines
  1465.         }
  1466.         IfInString, A_LoopField, Str
  1467.         {
  1468.             Attr := Attr . "Str" . Delim
  1469.             AttrValues := AttrValues . GetColonValue(A_LoopField) . Delim
  1470.         }
  1471.         IfInString, A_LoopField, Dex
  1472.         {
  1473.             Attr := Attr . "Dex" . Delim
  1474.             AttrValues := AttrValues . GetColonValue(A_LoopField) . Delim
  1475.         }
  1476.         IfInString, A_LoopField, Int
  1477.         {
  1478.             Attr := Attr . "Int" . Delim
  1479.             AttrValues := AttrValues . GetColonValue(A_LoopField) . Delim
  1480.         }
  1481.         IfInString, A_LoopField, Level
  1482.         {
  1483.             Level := GetColonValue(A_LoopField)
  1484.         }
  1485.     }
  1486.     ; Chop off last Delim
  1487.     If (SubStr(Attr, -(DelimLen-1)) == Delim)
  1488.     {
  1489.         Attr := SubStr(Attr, 1, -(DelimLen))
  1490.     }
  1491.     If (SubStr(AttrValues, -(DelimLen-1)) == Delim)
  1492.     {
  1493.         AttrValues := SubStr(AttrValues, 1, -(DelimLen))
  1494.     }
  1495.     Attributes := Attr
  1496.     Values := AttrValues
  1497. }
  1498.  
  1499. ; Parses #low-#high and sets Hi to #high and Lo to #low
  1500. ; if RangeChunk is just a single value (#) it will set both
  1501. ; Hi and Lo to this single value (effectively making the range 1-1 if # was 1)
  1502. ParseRange(RangeChunk, ByRef Hi, ByRef Lo)
  1503. {
  1504.     IfInString, RangeChunk, -
  1505.     {
  1506.         StringSplit, RangeParts, RangeChunk, -
  1507.         Lo := RegExReplace(RangeParts1, "(\d+?)", "$1")
  1508.         Hi := RegExReplace(RangeParts2, "(\d+?)", "$1")
  1509.     }
  1510.     Else
  1511.     {
  1512.         Hi := RangeChunk
  1513.         Lo := RangeChunk
  1514.     }
  1515. }
  1516.  
  1517. ParseItemLevel(ItemDataText)
  1518. {
  1519.     ; XXX
  1520.     ; Add support for The Awakening Closed Beta
  1521.     ; Once TA is released we won't need to support both occurences of
  1522.     ; the word "Item level" any more...
  1523.     ItemDataChunk := GetItemDataChunk(ItemDataText, "Itemlevel:")
  1524.     If (StrLen(ItemDataChunk) <= 0)
  1525.     {
  1526.         ItemDataChunk := GetItemDataChunk(ItemDataText, "Item Level:")
  1527.     }
  1528.    
  1529.     Assert(StrLen(ItemDataChunk) > 0, "ParseItemLevel: couldn't parse item data chunk")
  1530.    
  1531.     Loop, Parse, ItemDataChunk, `n, `r
  1532.     {
  1533.         IfInString, A_LoopField, Itemlevel:
  1534.         {
  1535.             StringSplit, ItemLevelParts, A_LoopField, %A_Space%
  1536.             Result := StrTrimWhitespace(ItemLevelParts2)
  1537.             return Result
  1538.         }
  1539.         IfInString, A_LoopField, Item Level:
  1540.         {
  1541.             StringSplit, ItemLevelParts, A_LoopField, %A_Space%
  1542.             Result := StrTrimWhitespace(ItemLevelParts3)
  1543.             return Result
  1544.         }
  1545.     }
  1546. }
  1547.  
  1548. ParseGemLevel(ItemDataText, PartialString="Level:")
  1549. {
  1550.     ItemDataChunk := GetItemDataChunk(ItemDataText, PartialString)
  1551.     Loop, Parse, ItemDataChunk, `n, `r
  1552.     {
  1553.         IfInString, A_LoopField, %PartialString%
  1554.         {
  1555.             StringSplit, ItemLevelParts, A_LoopField, %A_Space%
  1556.             Result := StrTrimWhitespace(ItemLevelParts2)
  1557.             return Result
  1558.         }
  1559.     }
  1560. }
  1561.  
  1562. StrMult(Char, Times)
  1563. {
  1564.     Result =
  1565.     Loop, %Times%
  1566.     {
  1567.         Result := Result . Char
  1568.     }
  1569.     return Result
  1570. }
  1571.  
  1572. StrTrimSpaceLeft(String)
  1573. {
  1574.     return RegExReplace(String, " *(.+?)", "$1")
  1575. }
  1576.  
  1577. StrTrimSpaceRight(String)
  1578. {
  1579.     return RegExReplace(String, "(.+?) *$", "$1")
  1580. }
  1581.  
  1582. StrTrimSpace(String)
  1583. {
  1584.     return RegExReplace(String, " *(.+?) *", "$1")
  1585. }
  1586.  
  1587. StrTrimWhitespace(String)
  1588. {
  1589.     return RegExReplace(String, "[ \r\n\t]*(.+?)[ \r\n\t]*", "$1")
  1590. }
  1591.  
  1592. ; Pads a string with a multiple of PadChar to become a wanted total length.
  1593. ; Note that Side is the side that is padded not the anchored side.
  1594. ; Meaning, if you pad right side, the text will move left. If Side was an
  1595. ; anchor instead, the text would move right if anchored right.
  1596. StrPad(String, Length, Side="right", PadChar=" ")
  1597. {
  1598.     StringLen, Len, String
  1599.     AddLen := Length-Len
  1600.     If (AddLen <= 0)
  1601.     {
  1602.         return String
  1603.     }
  1604.     Pad := StrMult(PadChar, AddLen)
  1605.     If (Side == "right")
  1606.     {
  1607.         Result := String . Pad
  1608.     }
  1609.     Else
  1610.     {
  1611.         Result := Pad . String
  1612.     }
  1613.     return Result
  1614. }
  1615.  
  1616. ; Prefix a string s with another string prefix.
  1617. ; Does nothing if s is already prefixed.
  1618. StrPrefix(s, prefix) {
  1619.     If (s == "") {
  1620.         return ""
  1621.     } Else {
  1622.         If (SubStr(s, 1, StrLen(prefix)) == prefix) {
  1623.             return s ; Nothing to do
  1624.         } Else {
  1625.             return prefix . s
  1626.         }
  1627.     }
  1628. }
  1629.  
  1630. BoolToString(flag) {
  1631.     If (flag == True) {
  1632.         return "True"
  1633.     } Else {
  1634.         return "False"
  1635.     }
  1636.     return "False"
  1637. }
  1638.  
  1639. ; Formats a number with SetFormat (leaving A_FormatFloat unchanged)
  1640. ; Returns formatted Num as string.
  1641. NumFormat(Num, Format)
  1642. {
  1643.     oldFormat := A_FormatFloat
  1644.     newNum := Num
  1645.     SetFormat, FloatFast, %Format%
  1646.     newNum += 0.0 ; convert to float, which applies SetFormat
  1647.     newNum := newNum . "" ; convert to string so the next SetFormat doesn't apply
  1648.     SetFormat, FloatFast, %oldFormat%
  1649.     return newNum
  1650. }
  1651.  
  1652. ; Pads a number with prefixed 0s and optionally rounds or appends to specified decimal places width.
  1653. NumPad(Num, TotalWidth, DecimalPlaces=0)
  1654. {
  1655.     myFormat = 0%TotalWidth%.%DecimalPlaces%
  1656.     newNum := NumFormat(Num, myFormat)
  1657.     return newNum
  1658. }
  1659.  
  1660. ; Estimate indicator, marks end user display values as guesstimated so they can take a look at it.
  1661. MarkAsGuesstimate(ValueRange, Side="left", Indicator=" * ")
  1662. {
  1663.     Global Globals, Opts
  1664.     Globals.Set("MarkedAsGuess", True)
  1665.     return StrPad(ValueRange . Indicator, Opts.ValueRangeFieldWidth + StrLen(Indicator), Side)
  1666. }
  1667.  
  1668. MakeAffixDetailLine(AffixLine, AffixType, ValueRange, Tier)
  1669. {
  1670.     Global ItemData
  1671.     Delim := "|" ; Internal delimiter, used as string split char later - do not change to the user adjustable delimiter
  1672.     Line := AffixLine . Delim . ValueRange . Delim . AffixType
  1673.     If (ItemData.Rarity == "Rare")
  1674.     {
  1675.         Line := Line . Delim . Tier
  1676.     }
  1677.     return Line
  1678. }
  1679.  
  1680. AppendAffixInfo(Line, AffixPos)
  1681. {
  1682.     Global AffixLines
  1683.     AffixLines.Set(AffixPos, Line)
  1684. }
  1685.  
  1686. AssembleAffixDetails()
  1687. {
  1688.     Global Opts, AffixLines
  1689.    
  1690.     AffixLine =
  1691.     AffixType =
  1692.     ValueRange =
  1693.     AffixTier =
  1694.     NumAffixLines := AffixLines.MaxIndex()
  1695.     AffixLineParts := 0
  1696.     Loop, %NumAffixLines%
  1697.     {
  1698.         CurLine := AffixLines[A_Index]
  1699.         ProcessedLine =
  1700.         Loop, %AffixLineParts0%
  1701.         {
  1702.             AffixLineParts%A_Index% =
  1703.         }
  1704.         StringSplit, AffixLineParts, CurLine, |
  1705.         AffixLine := AffixLineParts1
  1706.         ValueRange := AffixLineParts2
  1707.         AffixType := AffixLineParts3
  1708.         AffixTier := AffixLineParts4
  1709.  
  1710.         Delim := Opts.AffixDetailDelimiter
  1711.         Ellipsis := Opts.AffixDetailEllipsis
  1712.  
  1713.         If (Opts.ValueRangeFieldWidth > 0)
  1714.         {
  1715.             ValueRange := StrPad(ValueRange, Opts.ValueRangeFieldWidth, "left")
  1716.         }
  1717.         If (Opts.MirrorAffixLines == 1)
  1718.         {
  1719.             If (Opts.MirrorLineFieldWidth > 0)
  1720.             {
  1721.                 If(StrLen(AffixLine) > Opts.MirrorLineFieldWidth)
  1722.                 {  
  1723.                     AffixLine := StrTrimSpaceRight(SubStr(AffixLine, 1, Opts.MirrorLineFieldWidth)) . Ellipsis
  1724.                 }
  1725.                 AffixLine := StrPad(AffixLine, Opts.MirrorLineFieldWidth + StrLen(Ellipsis))
  1726.             }
  1727.             ProcessedLine := AffixLine . Delim
  1728.         }
  1729.         IfInString, ValueRange, *
  1730.         {
  1731.             ValueRangeString := StrPad(ValueRange, (Opts.ValueRangeFieldWidth * 2) + (StrLen(Opts.AffixDetailDelimiter)))
  1732.         }
  1733.         Else
  1734.         {
  1735.             ValueRangeString := ValueRange
  1736.         }
  1737.         ProcessedLine := ProcessedLine . ValueRangeString . Delim
  1738.         If (Opts.ShowAffixBracketTier == 1 and Not (ItemDataRarity == "Unique") and Not StrLen(AffixTier) = 0)
  1739.         {
  1740.             If (InStr(ValueRange, "*") and Opts.ShowAffixBracketTier)
  1741.             {
  1742.                 TierString := "   "
  1743.                 AdditionalPadding := ""
  1744.                 If (Opts.ShowAffixLevel or Opts.ShowAffixBracketTotalTier)
  1745.                 {
  1746.                     TierString := ""
  1747.                 }
  1748.                 If (Opts.ShowAffixLevel)
  1749.                 {
  1750.                     AdditionalPadding := AdditionalPadding . StrMult(" ", Opts.ValueRangeFieldWidth)
  1751.                 }
  1752.                 If (Opts.ShowAffixBracketTierTotal)
  1753.                 {
  1754.                     AdditionalPadding := AdditionalPadding . StrMult(" ", Opts.ValueRangeFieldWidth)
  1755.  
  1756.                 }
  1757.                 TierString := TierString . AdditionalPadding
  1758.             }
  1759.             Else
  1760.             {
  1761.                 AddedWidth := 0
  1762.                 If (Opts.ShowAffixBracketTierTotal)
  1763.                 {
  1764.                     AddedWidth += 2
  1765.  
  1766.                 }
  1767.                 TierString := StrPad("T" . AffixTier, 3+AddedWidth, "left")
  1768.             }
  1769.             ProcessedLine := ProcessedLine . TierString . Delim
  1770.         }
  1771.         ProcessedLine := ProcessedLine . AffixType . Delim
  1772.         Result := Result . "`n" . ProcessedLine
  1773.     }
  1774.     return Result
  1775. }
  1776.  
  1777. ; Same as AdjustRangeForQuality, except that Value is just
  1778. ; a single value and not a range.
  1779. AdjustValueForQuality(Value, ItemQuality, Direction="up")
  1780. {
  1781.     If (ItemQuality < 1)
  1782.         return Value
  1783.     Divisor := ItemQuality / 100
  1784.     If (Direction == "up")
  1785.     {
  1786.         Result := Round(Value + (Value * Divisor))
  1787.     }
  1788.     Else
  1789.     {
  1790.         Result := Round(Value - (Value * Divisor))
  1791.     }
  1792.     return Result
  1793. }
  1794.  
  1795. ; Adjust an affix' range for +% Quality on an item.
  1796. ; For example: given the range 10-20 and item quality +15%
  1797. ; the result would be 11.5-23 which is currently rounded up
  1798. ; to 12-23. Note that Direction does not play a part in rounding
  1799. ; rather it controls if adjusting up towards quality increase or
  1800. ; down from quality increase (to get the original value back)
  1801. AdjustRangeForQuality(ValueRange, ItemQuality, Direction="up")
  1802. {
  1803.     If (ItemQuality = 0)
  1804.     {
  1805.         return ValueRange
  1806.     }
  1807.     VRHi := 0
  1808.     VRLo := 0
  1809.     ParseRange(ValueRange, VRHi, VRLo)
  1810.     Divisor := ItemQuality / 100
  1811.     If (Direction == "up")
  1812.     {
  1813.         VRHi := Round(VRHi + (VRHi * Divisor))
  1814.         VRLo := Round(VRLo + (VRLo * Divisor))
  1815.     }
  1816.     Else
  1817.     {
  1818.         VRHi := Round(VRHi - (VRHi * Divisor))
  1819.         VRLo := Round(VRLo - (VRLo * Divisor))
  1820.     }
  1821.     If (VRLo == VRHi)
  1822.     {
  1823.         ValueRange = %VRLo%
  1824.     }
  1825.     Else
  1826.     {
  1827.         ValueRange = %VRLo%-%VRHi%
  1828.     }
  1829.     return ValueRange
  1830. }
  1831.  
  1832. ; Checks ActualValue against ValueRange, returning 1 if
  1833. ; ActualValue is within bounds of ValueRange, 0 otherwise.
  1834. WithinBounds(ValueRange, ActualValue)
  1835. {
  1836.     VHi := 0
  1837.     VLo := 0
  1838.     ParseRange(ValueRange, VHi, VLo)
  1839.     Result := 1
  1840.     IfInString, ActualValue, -
  1841.     {
  1842.         AVHi := 0
  1843.         AVLo := 0
  1844.         ParseRange(ActualValue, AVHi, AVLo)
  1845.         If ((AVLo < VLo) or (AVHi > VHi))
  1846.         {
  1847.             Result := 0
  1848.         }
  1849.     }
  1850.     Else
  1851.     {
  1852.         If ((ActualValue < VLo) or (ActualValue > VHi))
  1853.         {
  1854.             Result := 0
  1855.         }
  1856.     }
  1857.     return Result
  1858. }
  1859.  
  1860. GetAffixTypeFromProcessedLine(PartialAffixString)
  1861. {
  1862.     Global AffixLines
  1863.     NumAffixLines := AffixLines.MaxIndex()
  1864.     Loop, %NumAffixLines%
  1865.     {
  1866.         AffixLine := AffixLines[A_Index]
  1867.         IfInString, AffixLine, %PartialAffixString%
  1868.         {
  1869.             StringSplit, AffixLineParts, AffixLine, |
  1870.             return AffixLineParts3
  1871.         }
  1872.     }
  1873. }
  1874.  
  1875. ; Get actual value from a line of the ingame tooltip as a number
  1876. ; that can be used in calculations.
  1877. GetActualValue(ActualValueLine)
  1878. {
  1879.     Result := RegExReplace(ActualValueLine, ".*?\+?(\d+(?:-\d+|\.\d+)?).*", "$1")
  1880.     return Result
  1881. }
  1882.  
  1883. ; Get value from a colon line, e.g. given the line "Level: 57", returns the number 57
  1884. GetColonValue(Line)
  1885. {
  1886.     IfInString, Line, :
  1887.     {
  1888.         StringSplit, LineParts, Line, :
  1889.         Result := StrTrimSpace(LineParts%LineParts%2)
  1890.         return Result
  1891.     }
  1892. }
  1893.  
  1894. RangeMid(Range)
  1895. {
  1896.     If (Range = 0 or Range = "0" or Range = "0-0")
  1897.     {
  1898.         return 0
  1899.     }
  1900.     RHi := 0
  1901.     RLo := 0
  1902.     ParseRange(Range, RHi, RLo)
  1903.     RSum := RHi+RLo
  1904.     If (RSum == 0)
  1905.     {
  1906.         return 0
  1907.     }
  1908.     return Floor((RHi+RLo)/2)
  1909. }
  1910.  
  1911. RangeMin(Range)
  1912. {
  1913.     If (Range = 0 or Range = "0" or Range = "0-0")
  1914.     {
  1915.         return 0
  1916.     }
  1917.     RHi := 0
  1918.     RLo := 0
  1919.     ParseRange(Range, RHi, RLo)
  1920.     return RLo
  1921. }
  1922.  
  1923. RangeMax(Range)
  1924. {
  1925.     If (Range = 0 or Range = "0" or Range = "0-0")
  1926.     {
  1927.         return 0
  1928.     }
  1929.     RHi := 0
  1930.     RLo := 0
  1931.     ParseRange(Range, RHi, RLo)
  1932.     return RHi
  1933. }
  1934.  
  1935. AddRange(Range1, Range2)
  1936. {
  1937.     R1Hi := 0
  1938.     R1Lo := 0
  1939.     R2Hi := 0
  1940.     R2Lo := 0
  1941.     ParseRange(Range1, R1Hi, R1Lo)
  1942.     ParseRange(Range2, R2Hi, R2Lo)
  1943.     FinalHi := R1Hi + R2Hi
  1944.     FinalLo := R1Lo + R2Lo
  1945.     FinalRange = %FinalLo%-%FinalHi%
  1946.     return FinalRange
  1947. }
  1948.  
  1949. ; Used to check return values from LookupAffixBracket()
  1950. IsValidBracket(Bracket)
  1951. {
  1952.     If (Bracket == "n/a")
  1953.     {
  1954.         return False
  1955.     }
  1956.     return True
  1957. }
  1958.  
  1959. ; Used to check return values from LookupAffixData()
  1960. IsValidRange(Bracket)
  1961. {
  1962.     IfInString, Bracket, n/a
  1963.     {
  1964.         return False
  1965.     }
  1966.     return True
  1967. }
  1968.  
  1969. ; Note that while ExtractCompAffixBalance() can be run on processed data
  1970. ; that has compact affix type declarations (or not) for this function to
  1971. ; work properly, make sure to run it on data that has compact affix types
  1972. ; turned off. The reason being that it is hard to count prefixes by there
  1973. ; being a "P" in a line that also has mirrored affix descriptions.
  1974. ExtractTotalAffixBalance(ProcessedData, ByRef Prefixes, ByRef Suffixes, ByRef CompPrefixes, ByRef CompSuffixes)
  1975. {
  1976.     Loop, Parse, ProcessedData, `n, `r
  1977.     {
  1978.         AffixLine := A_LoopField
  1979.         IfInString, AffixLine, Comp. Prefix
  1980.         {
  1981.             CompPrefixes += 1
  1982.         }
  1983.         IfInString, AffixLine, Comp. Suffix
  1984.         {
  1985.             CompSuffixes += 1
  1986.         }
  1987.     }
  1988.     ProcessedData := RegExReplace(ProcessedData, "Comp\. Prefix", "")
  1989.     ProcessedData := RegExReplace(ProcessedData, "Comp\. Suffix", "")
  1990.     Loop, Parse, ProcessedData, `n, `r
  1991.     {
  1992.         AffixLine := A_LoopField
  1993.         IfInString, AffixLine, Prefix
  1994.         {
  1995.             Prefixes += 1
  1996.         }
  1997.         IfInString, AffixLine, Suffix
  1998.         {
  1999.             Suffixes += 1
  2000.         }
  2001.     }
  2002. }
  2003.  
  2004. ExtractCompositeAffixBalance(ProcessedData, ByRef CompPrefixes, ByRef CompSuffixes)
  2005. {
  2006.     Loop, Parse, ProcessedData, `n, `r
  2007.     {
  2008.         AffixLine := A_LoopField
  2009.         IfInString, AffixLine, Comp. Prefix
  2010.         {
  2011.             CompPrefixes += 1
  2012.         }
  2013.         IfInString, AffixLine, Comp. Suffix
  2014.         {
  2015.             CompSuffixes += 1
  2016.         }
  2017.     }
  2018. }
  2019.  
  2020. ParseFlaskAffixes(ItemDataAffixes)
  2021. {    
  2022.     Global AffixTotals
  2023.    
  2024.     IfInString, ItemDataChunk, Unidentified
  2025.     {
  2026.         return ; Not interested in unidentified items
  2027.     }
  2028.    
  2029.     NumPrefixes := 0
  2030.     NumSuffixes := 0
  2031.    
  2032.     Loop, Parse, ItemDataAffixes, `n, `r
  2033.     {
  2034.         If StrLen(A_LoopField) = 0
  2035.         {
  2036.             Continue ; Not interested in blank lines
  2037.         }
  2038.  
  2039.         ; Suffixes
  2040.        
  2041.         IfInString, A_LoopField, Dispels
  2042.         {
  2043.             ; Covers Shock, Burning and Frozen and Chilled
  2044.             If (NumSuffixes < 1)
  2045.             {
  2046.                 NumSuffixes += 1
  2047.             }
  2048.             Continue
  2049.         }
  2050.         IfInString, A_LoopField, Removes Bleeding
  2051.         {
  2052.             If (NumSuffixes < 1)
  2053.             {
  2054.                 NumSuffixes += 1
  2055.             }
  2056.             Continue
  2057.         }
  2058.         IfInString, A_LoopField, Removes Curses on use
  2059.         {
  2060.             If (NumSuffixes < 1)
  2061.             {
  2062.                 NumSuffixes += 1
  2063.             }
  2064.             Continue
  2065.         }
  2066.         IfInString, A_LoopField, during flask effect
  2067.         {
  2068.             If (NumSuffixes < 1)
  2069.             {
  2070.                 NumSuffixes += 1
  2071.             }
  2072.             Continue
  2073.         }
  2074.         IfInString, A_LoopField, Adds Knockback
  2075.         {
  2076.             If (NumSuffixes < 1)
  2077.             {
  2078.                 NumSuffixes += 1
  2079.             }
  2080.             Continue
  2081.         }
  2082.         IfInString, A_LoopField, Life Recovery to Minions
  2083.         {
  2084.             If (NumSuffixes < 1)
  2085.             {
  2086.                 NumSuffixes += 1
  2087.             }
  2088.             Continue
  2089.         }
  2090.        
  2091.         ; Prefixes
  2092.        
  2093.         IfInString, A_LoopField, Recovery Speed
  2094.         {
  2095.             If (NumPrefixes < 1)
  2096.             {
  2097.                 NumPrefixes += 1
  2098.             }
  2099.             Continue
  2100.         }
  2101.         IfInString, A_LoopField, Amount Recovered
  2102.         {
  2103.             If (NumPrefixes < 1)
  2104.             {
  2105.                 NumPrefixes += 1
  2106.             }
  2107.             Continue
  2108.         }
  2109.         IfInString, A_LoopField, Charges
  2110.         {
  2111.             If (NumPrefixes < 1)
  2112.             {
  2113.                 NumPrefixes += 1
  2114.             }
  2115.             Continue
  2116.         }
  2117.         IfInString, A_LoopField, Instant
  2118.         {
  2119.             If (NumPrefixes < 1)
  2120.             {
  2121.                 NumPrefixes += 1
  2122.             }
  2123.             Continue
  2124.         }
  2125.         IfInString, A_LoopField, Charge when
  2126.         {
  2127.             If (NumPrefixes < 1)
  2128.             {
  2129.                 NumPrefixes += 1
  2130.             }
  2131.             Continue
  2132.         }
  2133.         IfInString, A_LoopField, Recovery when
  2134.         {
  2135.             If (NumPrefixes < 1)
  2136.             {
  2137.                 NumPrefixes += 1
  2138.             }
  2139.             Continue
  2140.         }
  2141.         IfInString, A_LoopField, Mana Recovered
  2142.         {
  2143.             If (NumPrefixes < 1)
  2144.             {
  2145.                 NumPrefixes += 1
  2146.             }
  2147.             Continue
  2148.         }
  2149.         IfInString, A_LoopField, Life Recovered
  2150.         {
  2151.             If (NumPrefixes < 1)
  2152.             {
  2153.                 NumPrefixes += 1
  2154.             }
  2155.             Continue
  2156.         }
  2157.     }
  2158.    
  2159.     AffixTotals.NumPrefixes := NumPrefixes
  2160.     AffixTotals.NumSuffixes := NumSuffixes
  2161. }
  2162.  
  2163. ; Try looking up the remainder bracket based on Bracket
  2164. ; This is done by calculating the rest value in three
  2165. ; different ways, falling through if not successful:
  2166. ;
  2167. ; 1) CurrValue - RangeMid(Bracket)
  2168. ; 2) CurrValue - RangeMin(Bracket)
  2169. ; 3) CurrValue - RangeMax(Bracket)
  2170. ;
  2171. ; (Internal: RegExr x-forms):
  2172. ;
  2173. ; with ByRef BracketLevel:
  2174. ;   ( *)(.+Rest) := CurrValue - RangeMid\((.+)\)\r *(.+) := LookupAffixBracket\((.+?), (.+?), (.+?), (.+?)\)
  2175. ;   -> $1$4 := LookupRemainingAffixBracket($5, $6, CurrValue, $3, $8)
  2176. ;
  2177. ; w/o ByRef BracketLevel:
  2178. ;   ( *)(.+Rest) := CurrValue - RangeMid\((.+)\)\r *(.+) := LookupAffixBracket\((.+?), (.+?), (.+?)\)
  2179. ;   -> $1$4 := LookupRemainingAffixBracket($5, $6, CurrValue, $3)
  2180. ;
  2181. LookupRemainingAffixBracket(Filename, ItemLevel, CurrValue, Bracket, ByRef BracketLevel=0)
  2182. {
  2183.     RestValue := CurrValue - RangeMid(Bracket)
  2184.     RemainderBracket := LookupAffixBracket(Filename, ItemLevel, RestValue, BracketLevel)
  2185.     If (Not IsValidBracket(RemainderBracket))
  2186.     {
  2187.         RestValue := CurrValue - RangeMin(Bracket)
  2188.         RemainderBracket := LookupAffixBracket(Filename, ItemLevel, RestValue, BracketLevel)
  2189.     }
  2190.     If (Not IsValidBracket(RemainderBracket))
  2191.     {
  2192.         RestValue := CurrValue - RangeMax(Bracket)
  2193.         RemainderBracket := LookupAffixBracket(Filename, ItemLevel, RestValue, BracketLevel)
  2194.     }
  2195.     return RemainderBracket
  2196. }
  2197.  
  2198. ParseAffixes(ItemDataAffixes, Item)
  2199. {
  2200.     Global Globals, Opts, AffixTotals
  2201.  
  2202.     ItemDataChunk := ItemDataAffixes
  2203.  
  2204.     ItemBaseType := Item.BaseType
  2205.     ItemSubType := Item.SubType
  2206.     ItemGripType := Item.GripType
  2207.     ItemLevel := Item.Level
  2208.     ItemQuality := Item.Quality
  2209.    
  2210.      ; Reset the AffixLines "array" and other vars
  2211.     ResetAffixDetailVars()
  2212.  
  2213.     ; Keeps track of how many affix lines we have so they can be assembled later.
  2214.     ; Acts as a loop index variable when iterating each affix data part.
  2215.     NumPrefixes := 0
  2216.     NumSuffixes := 0
  2217.    
  2218.     ; Composition flags
  2219.     ;
  2220.     ; These are required for descision making later, when guesstimating
  2221.     ; sources for parts of a value from composite and/or same name affixes.
  2222.     ; They will be set to the line number where they occur in the pre-pass
  2223.     ; loop, so that details for that line can be changed later after we
  2224.     ; have more clues for possible compositions.
  2225.     HasIIQ := 0
  2226.     HasIncrArmour := 0
  2227.     HasIncrEvasion := 0
  2228.     HasIncrEnergyShield := 0
  2229.     HasHybridDefences := 0
  2230.     HasIncrArmourAndES := 0
  2231.     HasIncrArmourAndEvasion := 0
  2232.     HasIncrEvasionAndES := 0
  2233.     HasIncrLightRadius := 0
  2234.     HasIncrAccuracyRating := 0
  2235.     HasIncrPhysDmg := 0
  2236.     HasToAccuracyRating := 0
  2237.     HasStunRecovery := 0
  2238.     HasSpellDamage := 0
  2239.     HasMaxMana := 0
  2240.     HasMultipleCrafted := 0
  2241.  
  2242.     ; Max mana already accounted for in case of Composite Prefix+Prefix
  2243.     ; "Spell Damage / Max Mana" + "Max Mana"
  2244.     MaxManaPartial =
  2245.  
  2246.     ; Accuracy Rating already accounted for in case of
  2247.     ;   Composite Prefix + Composite Suffix:
  2248.     ;       "increased Physical Damage / to Accuracy Rating" +
  2249.     ;       "to Accuracy Rating / Light Radius"
  2250.     ;   Composite Prefix + Suffix:
  2251.     ;       "increased Physical Damage / to Accuracy Rating" +
  2252.     ;       "to Accuracy Rating"
  2253.     ARPartial =
  2254.     ARAffixTypePartial =
  2255.  
  2256.     ; Partial for the former "Block and Stun Recovery"
  2257.     ; Note: with PoE v1.3+ now called just "increased Stun Recovery"
  2258.     BSRecPartial =
  2259.  
  2260.     ; --- PRE-PASS ---
  2261.    
  2262.     ; To determine composition flags
  2263.     Loop, Parse, ItemDataChunk, `n, `r
  2264.     {    
  2265.         If StrLen(A_LoopField) = 0
  2266.         {
  2267.             Break ; Not interested in blank lines
  2268.         }
  2269.         IfInString, ItemDataChunk, Unidentified
  2270.         {
  2271.             Break ; Not interested in unidentified items
  2272.         }
  2273.                
  2274.         IfInString, A_LoopField, increased Light Radius
  2275.         {
  2276.             HasIncrLightRadius := A_Index
  2277.             Continue
  2278.         }
  2279.         IfInString, A_LoopField, increased Quantity
  2280.         {
  2281.             HasIIQ := A_Index
  2282.             Continue
  2283.         }
  2284.         IfInString, A_LoopField, increased Physical Damage
  2285.         {
  2286.             HasIncrPhysDmg := A_Index
  2287.             Continue
  2288.         }
  2289.         IfInString, A_LoopField, increased Accuracy Rating
  2290.         {
  2291.             HasIncrAccuracyRating := A_Index
  2292.             Continue
  2293.         }
  2294.         IfInString, A_LoopField, to Accuracy Rating
  2295.         {
  2296.             HasToAccuracyRating := A_Index
  2297.             Continue
  2298.         }
  2299.         IfInString, A_LoopField, increased Armour and Evasion
  2300.         {
  2301.             HasHybridDefences := A_Index
  2302.             HasIncrArmourAndEvasion := A_Index
  2303.             Continue
  2304.         }
  2305.         IfInString, A_LoopField, increased Armour and Energy Shield
  2306.         {
  2307.             HasHybridDefences := A_Index
  2308.             HasIncrArmourAndES := A_Index
  2309.             Continue
  2310.         }
  2311.         IfInString, A_LoopField, increased Evasion and Energy Shield
  2312.         {
  2313.             HasHybridDefences := A_Index
  2314.             HasIncrEvasionAndES := A_Index
  2315.             Continue
  2316.         }
  2317.         IfInString, A_LoopField, increased Armour
  2318.         {
  2319.             HasIncrArmour := A_Index
  2320.             Continue
  2321.         }
  2322.         IfInString, A_LoopField, increased Evasion Rating
  2323.         {
  2324.             HasIncrEvasion := A_Index
  2325.             Continue
  2326.         }
  2327.         IfInString, A_LoopField, increased Energy Shield
  2328.         {
  2329.             HasIncrEnergyShield := A_Index
  2330.             Continue
  2331.         }
  2332.         IfInString, A_LoopField, increased Stun Recovery
  2333.         {
  2334.             HasStunRecovery := A_Index
  2335.             Continue
  2336.         }
  2337.         IfInString, A_LoopField, increased Spell Damage
  2338.         {
  2339.             HasSpellDamage := A_Index
  2340.             Continue
  2341.         }
  2342.         IfInString, A_LoopField, to maximum Mana
  2343.         {
  2344.             HasMaxMana := A_Index
  2345.             Continue
  2346.         }
  2347.         IfInString, A_Loopfield, Can have multiple Crafted Mods
  2348.         {
  2349.             HasMultipleCrafted := A_Index
  2350.             Continue
  2351.         }
  2352.     }
  2353.  
  2354.     ; Note: yes, these superlong IfInString structures suck, but hey,
  2355.     ; AHK sucks as an object-oriented scripting language, so bite me.
  2356.     ;
  2357.     ; But in all seriousness, there are two main parts - Simple and
  2358.     ; Complex Affixes - which could be refactored into their own helper
  2359.     ; methods.
  2360.    
  2361.     ; --- SIMPLE AFFIXES ---
  2362.  
  2363.     Loop, Parse, ItemDataChunk, `n, `r
  2364.     {
  2365.         If StrLen(A_LoopField) = 0
  2366.         {
  2367.             Break ; Not interested in blank lines
  2368.         }
  2369.         IfInString, ItemDataChunk, Unidentified
  2370.         {
  2371.             Break ; Not interested in unidentified items
  2372.         }
  2373.        
  2374.         CurrValue := GetActualValue(A_LoopField)
  2375.         CurrTier := 0
  2376.         BracketLevel := 0
  2377.  
  2378.         ; Suffixes
  2379.  
  2380.         IfInString, A_LoopField, increased Attack Speed
  2381.         {
  2382.             NumSuffixes += 1
  2383.             If (ItemBaseType == "Weapon") ; ItemBaseType is Global!
  2384.             {
  2385.                 ValueRange := LookupAffixData("data\AttackSpeed_Weapons.txt", ItemLevel, CurrValue, "", CurrTier)
  2386.             }
  2387.             Else
  2388.             {
  2389.                 ValueRange := LookupAffixData("data\AttackSpeed_ArmourAndItems.txt", ItemLevel, CurrValue, "", CurrTier)
  2390.             }
  2391.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2392.             Continue
  2393.         }
  2394.         IfInString, A_LoopField, increased Accuracy Rating
  2395.         {
  2396.             AffixType := "Comp. Suffix"
  2397.             ValueRange := LookupAffixData("data\IncrAccuracyRating_LightRadius.txt", ItemLevel, CurrValue, "", CurrTier)
  2398.             NumSuffixes += 1
  2399.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  2400.             Continue
  2401.         }
  2402.  
  2403.         IfInString, A_LoopField, to all Attributes
  2404.         {
  2405.             NumSuffixes += 1
  2406.             ValueRange := LookupAffixData("data\ToAllAttributes.txt", ItemLevel, CurrValue, "", CurrTier)
  2407.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2408.             Continue
  2409.         }
  2410.         IfInString, A_LoopField, to Strength
  2411.         {
  2412.             NumSuffixes += 1
  2413.             ValueRange := LookupAffixData("data\ToStrength.txt", ItemLevel, CurrValue, "", CurrTier)
  2414.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2415.             Continue
  2416.         }
  2417.         IfInString, A_LoopField, to Intelligence
  2418.         {
  2419.             NumSuffixes += 1
  2420.             ValueRange := LookupAffixData("data\ToIntelligence.txt", ItemLevel, CurrValue, "", CurrTier)
  2421.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2422.             Continue
  2423.         }
  2424.         IfInString, A_LoopField, to Dexterity
  2425.         {
  2426.             NumSuffixes += 1
  2427.             ValueRange := LookupAffixData("data\ToDexterity.txt", ItemLevel, CurrValue, "", CurrTier)
  2428.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2429.             Continue
  2430.         }
  2431.         IfInString, A_LoopField, increased Cast Speed
  2432.         {
  2433.             NumSuffixes += 1
  2434.             ValueRange := LookupAffixData("data\CastSpeed.txt", ItemLevel, CurrValue, "", CurrTier)
  2435.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2436.             Continue
  2437.         }
  2438.         ; This needs to come before "Critical Strike Chance" !
  2439.         IfInString, A_LoopField, increased Critical Strike Chance for Spells
  2440.         {
  2441.             NumSuffixes += 1
  2442.             ValueRange := LookupAffixData("data\SpellCritChance.txt", ItemLevel, CurrValue, "", CurrTier)
  2443.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2444.             Continue
  2445.         }
  2446.         IfInString, A_LoopField, Critical Strike Chance
  2447.         {
  2448.             If (ItemSubType == "Quiver" or ItemSubType == "Amulet")
  2449.             {
  2450.                 ValueRange := LookupAffixData("data\CritChance_AmuletsAndQuivers.txt", ItemLevel, CurrValue, "", CurrTier)
  2451.             }
  2452.             Else
  2453.             {
  2454.                 ValueRange := LookupAffixData("data\CritChance_Weapons.txt", ItemLevel, CurrValue, "", CurrTier)
  2455.             }
  2456.             NumSuffixes += 1
  2457.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2458.             Continue
  2459.         }
  2460.         IfInString, A_LoopField, Critical Strike Multiplier
  2461.         {
  2462.             If (ItemSubType == "Quiver" or ItemSubType == "Amulet")
  2463.             {
  2464.                 ValueRange := LookupAffixData("data\CritMultiplier_AmuletsAndQuivers.txt", ItemLevel, CurrValue, "", CurrTier)
  2465.             }
  2466.             Else
  2467.             {
  2468.                 ValueRange := LookupAffixData("data\CritMultiplier_Weapons.txt", ItemLevel, CurrValue, "", CurrTier)
  2469.             }
  2470.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2471.             NumSuffixes += 1
  2472.             Continue
  2473.         }
  2474.         IfInString, A_LoopField, increased Fire Damage
  2475.         {
  2476.             NumSuffixes += 1
  2477.             ValueRange := LookupAffixData("data\IncrFireDamage.txt", ItemLevel, CurrValue, "", CurrTier)
  2478.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2479.             Continue
  2480.         }
  2481.         IfInString, A_LoopField, increased Cold Damage
  2482.         {
  2483.             NumSuffixes += 1
  2484.             ValueRange := LookupAffixData("data\IncrColdDamage.txt", ItemLevel, CurrValue, "", CurrTier)
  2485.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2486.             Continue
  2487.         }
  2488.         IfInString, A_LoopField, increased Lightning Damage
  2489.         {
  2490.             NumSuffixes += 1
  2491.             ValueRange := LookupAffixData("data\IncrLightningDamage.txt", ItemLevel, CurrValue, "", CurrTier)
  2492.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2493.             Continue
  2494.         }
  2495.         IfInString, A_LoopField, increased Light Radius
  2496.         {
  2497.             ValueRange := LookupAffixData("data\LightRadius_AccuracyRating.txt", ItemLevel, CurrValue, "", CurrTier)
  2498.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Comp. Suffix", ValueRange, CurrTier), A_Index)
  2499.             Continue
  2500.         }
  2501.         IfInString, A_LoopField, Block Chance
  2502.         {
  2503.             NumSuffixes += 1
  2504.             ValueRange := LookupAffixData("data\BlockChance.txt", ItemLevel, CurrValue, "", CurrTier)
  2505.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2506.             Continue
  2507.         }
  2508.        
  2509.         ; Flask affixes (on belts)
  2510.         IfInString, A_LoopField, reduced Flask Charges used
  2511.         {
  2512.             NumSuffixes += 1
  2513.             ValueRange := LookupAffixData("data\FlaskChargesUsed.txt", ItemLevel, CurrValue, "", CurrTier)
  2514.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2515.             Continue
  2516.         }
  2517.         IfInString, A_LoopField, increased Flask Charges gained
  2518.         {
  2519.             NumSuffixes += 1
  2520.             ValueRange := LookupAffixData("data\FlaskChargesGained.txt", ItemLevel, CurrValue, "", CurrTier)
  2521.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2522.             Continue
  2523.         }
  2524.         IfInString, A_LoopField, increased Flask effect duration
  2525.         {
  2526.             NumSuffixes += 1
  2527.             ValueRange := LookupAffixData("data\FlaskDuration.txt", ItemLevel, CurrValue, "", CurrTier)
  2528.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2529.             Continue
  2530.         }
  2531.        
  2532.         IfInString, A_LoopField, increased Quantity
  2533.         {
  2534.             NumSuffixes += 1
  2535.             ValueRange := LookupAffixData("data\IIQ.txt", ItemLevel, CurrValue, "", CurrTier)
  2536.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2537.             Continue
  2538.         }
  2539.         IfInString, A_LoopField, Life gained on Kill
  2540.         {
  2541.             NumSuffixes += 1
  2542.             ValueRange := LookupAffixData("data\LifeOnKill.txt", ItemLevel, CurrValue, "", CurrTier)
  2543.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2544.             Continue
  2545.         }
  2546.         IfInString, A_LoopField, Life gained for each Enemy hit by your Attacks
  2547.         {
  2548.             NumSuffixes += 1
  2549.             ValueRange := LookupAffixData("data\LifeOnHit.txt", ItemLevel, CurrValue, "", CurrTier)
  2550.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2551.             Continue
  2552.         }
  2553.         IfInString, A_LoopField, Life Regenerated per second
  2554.         {
  2555.             NumSuffixes += 1
  2556.             ValueRange := LookupAffixData("data\LifeRegen.txt", ItemLevel, CurrValue, "", CurrTier)
  2557.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2558.             Continue
  2559.         }
  2560.         IfInString, A_LoopField, Mana Gained on Kill
  2561.         {
  2562.             NumSuffixes += 1
  2563.             ValueRange := LookupAffixData("data\ManaOnKill.txt", ItemLevel, CurrValue, "", CurrTier)
  2564.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2565.             Continue
  2566.         }
  2567.         IfInString, A_LoopField, increased Mana Regeneration Rate
  2568.         {
  2569.             NumSuffixes += 1
  2570.             ValueRange := LookupAffixData("data\ManaRegen.txt", ItemLevel, CurrValue, "", CurrTier)
  2571.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2572.             Continue
  2573.         }
  2574.         IfInString, A_LoopField, increased Projectile Speed
  2575.         {
  2576.             NumSuffixes += 1
  2577.             ValueRange := LookupAffixData("data\ProjectileSpeed.txt", ItemLevel, CurrValue, "", CurrTier)
  2578.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2579.             Continue
  2580.         }
  2581.         IfInString, A_LoopField, reduced Attribute Requirements
  2582.         {
  2583.             NumSuffixes += 1
  2584.             ValueRange := LookupAffixData("data\ReducedAttrReqs.txt", ItemLevel, CurrValue, "", CurrTier)
  2585.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2586.             Continue
  2587.         }
  2588.         IfInString, A_LoopField, to all Elemental Resistances
  2589.         {
  2590.             NumSuffixes += 1
  2591.             ValueRange := LookupAffixData("data\AllResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2592.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2593.             Continue
  2594.         }
  2595.         IfInString, A_LoopField, to Fire Resistance
  2596.         {
  2597.             NumSuffixes += 1
  2598.             ValueRange := LookupAffixData("data\FireResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2599.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2600.             Continue
  2601.         }
  2602.         IfInString, A_LoopField, to Lightning Resistance
  2603.         {
  2604.             NumSuffixes += 1
  2605.             ValueRange := LookupAffixData("data\LightningResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2606.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2607.             Continue
  2608.         }
  2609.         IfInString, A_LoopField, to Cold Resistance
  2610.         {
  2611.             NumSuffixes += 1
  2612.             ValueRange := LookupAffixData("data\ColdResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2613.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2614.             Continue
  2615.         }
  2616.         IfInString, A_LoopField, to Chaos Resistance
  2617.         {
  2618.             NumSuffixes += 1
  2619.             ValueRange := LookupAffixData("data\ChaosResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2620.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2621.             Continue
  2622.         }
  2623.         If RegExMatch(A_LoopField, ".*to (Cold|Fire|Lightning) and (Cold|Fire|Lightning) Resistances")
  2624.         {
  2625.             ; Catches two-stone rings and the like which have "+#% to Cold and Lightning Resistances"
  2626.             IfInString, A_LoopField, Fire
  2627.             {
  2628.                 NumSuffixes += 1
  2629.                 ValueRange := LookupAffixData("data\FireResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2630.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2631.                 Continue
  2632.             }
  2633.             IfInString, A_LoopField, Lightning
  2634.             {
  2635.                 NumSuffixes += 1
  2636.                 ValueRange := LookupAffixData("data\LightningResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2637.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2638.                 Continue
  2639.             }
  2640.             IfInString, A_LoopField, Cold
  2641.             {
  2642.                 NumSuffixes += 1
  2643.                 ValueRange := LookupAffixData("data\ColdResist.txt", ItemLevel, CurrValue, "", CurrTier)
  2644.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2645.                 Continue
  2646.             }
  2647.         }
  2648.         IfInString, A_LoopField, increased Stun Duration on Enemies
  2649.         {
  2650.             NumSuffixes += 1
  2651.             ValueRange := LookupAffixData("data\StunDuration.txt", ItemLevel, CurrValue, "", CurrTier)
  2652.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2653.             Continue
  2654.         }
  2655.         IfInString, A_LoopField, reduced Enemy Stun Threshold
  2656.         {
  2657.             NumSuffixes += 1
  2658.             ValueRange := LookupAffixData("data\StunThreshold.txt", ItemLevel, CurrValue, "", CurrTier)
  2659.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  2660.             Continue
  2661.         }
  2662.        
  2663.         ; Prefixes
  2664.        
  2665.         IfInString, A_LoopField, to Armour
  2666.         {
  2667.             NumPrefixes += 1
  2668.             If (ItemBaseType == "Item")
  2669.             {
  2670.                 ; Global
  2671.                 ValueRange := LookupAffixData("data\ToArmour_Items.txt", ItemLevel, CurrValue, "", CurrTier)
  2672.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  2673.             }
  2674.             Else
  2675.             {
  2676.                 ; Local
  2677.                 ValueRange := LookupAffixData("data\ToArmour_WeaponsAndArmour.txt", ItemLevel, CurrValue, "", CurrTier)
  2678.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  2679.             }
  2680.             Continue
  2681.         }
  2682.         IfInString, A_LoopField, increased Armour and Evasion
  2683.         {
  2684.             AffixType := "Prefix"
  2685.             AEBracketLevel := 0
  2686.             ValueRange := LookupAffixData("data\ArmourAndEvasion.txt", ItemLevel, CurrValue, AEBracketLevel, CurrTier)
  2687.             If (HasStunRecovery)
  2688.             {
  2689.                 AEBracketLevel2 := AEBracketLevel
  2690.  
  2691.                 AEBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue, AEBracketLevel2)
  2692.                 If (Not IsValidRange(ValueRange) and IsValidBracket(AEBracket))
  2693.                 {
  2694.                     ValueRange := LookupAffixData("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue, AEBracketLevel2, CurrTier)
  2695.                 }
  2696.                 AffixType := "Comp. Prefix"
  2697.                 BSRecBracketLevel := 0
  2698.                 BSRecValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Stun Recovery")
  2699.                 BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", AEBracketLevel2, "", BSRecBracketLevel)
  2700.                 If (Not IsValidBracket(BSRecPartial) or Not IsValidBracket(AEBracket))
  2701.                 {
  2702.                     ; This means that we are actually dealing with a Prefix + Comp. Prefix.
  2703.                     ; To get the part for the hybrid defence that is contributed by the straight prefix,
  2704.                     ; lookup the bracket level for the B&S Recovery line and then work out the partials
  2705.                     ; for the hybrid stat from the bracket level of B&S Recovery.
  2706.                     ;
  2707.                     ; For example:
  2708.                     ;   87% increased Armour and Evasion
  2709.                     ;   7% increased Stun Recovery
  2710.                     ;
  2711.                     ;   1) 7% B&S indicates bracket level 2 (6-7)
  2712.                     ;   2) Lookup bracket level 2 from the hybrid stat + block and stun recovery table
  2713.                     ;      This works out to be 6-14.
  2714.                     ;   3) Subtract 6-14 from 87 to get the rest contributed by the hybrid stat as pure prefix.
  2715.                     ;
  2716.                     ; Currently when subtracting a range from a single value we just use the range's
  2717.                     ; max as single value. This may need changing depending on circumstance but it
  2718.                     ; works for now. EDIT: no longer the case, now uses RangeMid(...). EDIT2: Rest value calc
  2719.                     ; now routed through LookupRemainingAffixBracket() which uses trickle-down through all
  2720.                     ; three Range... functions. #'s below NOT YET changed to reflect that...
  2721.                     ;   87-10 = 77
  2722.                     ;   4) lookup affix data for increased Armour and Evasion with value of 77
  2723.                     ;
  2724.                     ; We now know, this is a Comp. Prefix+Prefix
  2725.                     ;
  2726.                     BSRecBracketLevel := 0
  2727.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", ItemLevel, BSRecValue, BSRecBracketLevel)
  2728.                     If (Not IsValidBracket(BSRecPartial))
  2729.                     {
  2730.                         ; This means that the hybrid stat is a Comp. Prefix (Hybrid)+Prefix and SR is a Comp. Prefix (Hybrid)+Suffix.
  2731.                         ;
  2732.                         ; For example the following case:
  2733.                         ;   Item Level: 58
  2734.                         ;   107% increased Armour and Evasion (AE)
  2735.                         ;   ...
  2736.                         ;   30% increased Stun Recovery (SR)
  2737.                         ;
  2738.                         ; Based on item level, 33-41 is the max contribution for AE of HybridDefences_StunRecovery (Comp. Prefix),
  2739.                         ; 12-13 is the max contribution for Stun Rec of StunRecovery_Hybrid (Comp. Prefix), 23-25 is the max contribution
  2740.                         ; for SR of StunRecovery_Suffix (Suffix)
  2741.                         ;
  2742.                         ; Obviously this is ambiguous and tough to resolve, but we'll try anyway...
  2743.                         ;
  2744.                         BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", ItemLevel, "", BSRecBracketLevel)
  2745.                     }
  2746.                    
  2747.                     AEBSBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", BSRecBracketLevel)
  2748.  
  2749.                     If (Not WithinBounds(AEBSBracket, CurrValue))
  2750.                     {                        
  2751.                         AEBracket := LookupRemainingAffixBracket("data\ArmourAndEvasion.txt", ItemLevel, CurrValue, AEBSBracket)
  2752.  
  2753.                         If (Not IsValidBracket(AEBracket))
  2754.                         {
  2755.                             AEBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue)
  2756.                         }
  2757.                         If (IsValidBracket(AEBracket) and WithinBounds(AEBracket, CurrValue))
  2758.                         {
  2759.                             If (NumPrefixes < 2)
  2760.                             {
  2761.                                 ValueRange := AddRange(AEBSBracket, AEBracket)
  2762.                                 ValueRange := MarkAsGuesstimate(ValueRange)
  2763.                                 AffixType := "Comp. Prefix+Prefix"
  2764.                                 NumPrefixes += 1
  2765.                             }
  2766.                             Else
  2767.                             {
  2768.                                 ValueRange := LookupAffixData("data\ArmourAndEvasion.txt", ItemLevel, CurrValue, AEBracketLevel2, CurrTier)
  2769.                                 AffixType := "Prefix"
  2770.                             }
  2771.                         }
  2772.                         Else
  2773.                         {                
  2774.                             ; Check if it isn't a simple case of Armour and Evasion (Prefix) + Stun Recovery (Suffix)
  2775.                             BSRecBracket := LookupAffixBracket("data\StunRecovery_Suffix.txt", ItemLevel, BSRecValue, BSRecBracketLevel, CurrTier)
  2776.                             If (IsValidRange(ValueRange) and IsValidBracket(BSRecBracket))
  2777.                             {
  2778.                                 ; -2 means for later that processing this hybrid defence stat
  2779.                                 ; determined that Stun Recovery should be a simple suffix
  2780.                                 BSRecPartial := ""
  2781.                                 AffixType := "Prefix"
  2782.                                 ValueRange := LookupAffixData("data\ArmourAndEvasion.txt", ItemLevel, CurrValue, AEBracketLevel, CurrTier)
  2783.                             }
  2784.                         }
  2785.                     }
  2786.  
  2787.                     If (WithinBounds(BSRecPartial, BSRecValue))
  2788.                     {
  2789.                         ; BS Recovery value within bounds, this means BS Rec is all acounted for
  2790.                         BSRecPartial =
  2791.                     }
  2792.                 }
  2793.             }
  2794.             NumPrefixes += 1
  2795.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  2796.             Continue
  2797.         }
  2798.         IfInString, A_LoopField, increased Armour and Energy Shield
  2799.         {
  2800.             AffixType := "Prefix"
  2801.             AESBracketLevel := 0
  2802.             ValueRange := LookupAffixData("data\ArmourAndEnergyShield.txt", ItemLevel, CurrValue, AESBracketLevel, CurrTier)
  2803.             If (HasStunRecovery)
  2804.             {
  2805.                 AESBracketLevel2 := AESBracketLevel
  2806.  
  2807.                 AESBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue, AESBracketLevel2)
  2808.                 If (Not IsValidRange(ValueRange) and IsValidBracket(AESBracket))
  2809.                 {
  2810.                     ValueRange := LookupAffixData("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue, AESBracketLevel2, CurrTier)
  2811.                 }
  2812.                 AffixType := "Comp. Prefix"
  2813.                 BSRecBracketLevel := 0
  2814.                 BSRecValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Stun Recovery")
  2815.                 BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", AESBracketLevel2, "", BSRecBracketLevel)
  2816.                 If (Not IsValidBracket(BSRecPartial) or Not IsValidBracket(AESBracket))
  2817.                 {
  2818.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Armour.txt", AESBracketLevel, "", BSRecBracketLevel)
  2819.                 }
  2820.                 If (Not IsValidBracket(BSRecPartial))
  2821.                 {
  2822.                     BSRecBracketLevel := 0
  2823.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", ItemLevel, BSRecValue, BSRecBracketLevel)
  2824.                     If (Not IsValidBracket(BSRecPartial))
  2825.                     {
  2826.                         BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", ItemLevel, "", BSRecBracketLevel)
  2827.                     }
  2828.  
  2829.                     AESBSBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", BSRecBracketLevel)
  2830.  
  2831.                     If (Not WithinBounds(AESBSBracket, CurrValue))
  2832.                     {
  2833.                         AESBracket := LookupRemainingAffixBracket("data\ArmourAndEnergyShield.txt", ItemLevel, CurrValue, AESBSBracket)
  2834.                         If (Not IsValidBracket(AESBracket))
  2835.                         {
  2836.                             AESBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue)
  2837.                         }
  2838.                         If (Not WithinBounds(AESBracket, CurrValue))
  2839.                         {
  2840.                             ValueRange := AddRange(AESBSBracket, AESBracket)
  2841.                             ValueRange := MarkAsGuesstimate(ValueRange)
  2842.                             AffixType := "Comp. Prefix+Prefix"
  2843.                             NumPrefixes += 1
  2844.                         }
  2845.                     }
  2846.                     If (WithinBounds(BSRecPartial, BSRecValue))
  2847.                     {
  2848.                         ; BS Recovery value within bounds, this means BS Rec is all acounted for
  2849.                         BSRecPartial =
  2850.                     }
  2851.                 }
  2852.             }
  2853.             NumPrefixes += 1
  2854.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  2855.             Continue
  2856.         }
  2857.         IfInString, A_LoopField, increased Evasion and Energy Shield
  2858.         {
  2859.             AffixType := "Prefix"
  2860.             EESBracketLevel := 0
  2861.             ValueRange := LookupAffixData("data\EvasionAndEnergyShield.txt", ItemLevel, CurrValue, EESBracketLevel, CurrTier)
  2862.             If (HasStunRecovery)
  2863.             {
  2864.                 EESBracketLevel2 := EESBracketLevel
  2865.  
  2866.                 EESBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue, EESBracketLevel2)
  2867.                 If (Not IsValidRange(ValueRange) and IsValidBracket(EESBracket))
  2868.                 {
  2869.                     ValueRange := LookupAffixData("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue, EESBracketLevel2, CurrTier)
  2870.                 }
  2871.  
  2872.                 AffixType := "Comp. Prefix"
  2873.                 BSRecBracketLevel := 0
  2874.                 BSRecValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Stun Recovery")
  2875.                 BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", EESBracketLevel2, "", BSRecBracketLevel)
  2876.                 If (Not IsValidBracket(BSRecPartial) or Not IsValidBracket(EESBracket))
  2877.                 {
  2878.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", EESBracketLevel, "", BSRecBracketLevel)
  2879.                 }
  2880.                 If (Not IsValidBracket(BSRecPartial))
  2881.                 {
  2882.                     BSRecBracketLevel := 0
  2883.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", ItemLevel, BSRecValue, BSRecBracketLevel)
  2884.                     If (Not IsValidBracket(BSRecPartial))
  2885.                     {
  2886.                         BSRecPartial := LookupAffixBracket("data\StunRecovery_Hybrid.txt", ItemLevel, "", BSRecBracketLevel)
  2887.                     }
  2888.  
  2889.                     EESBSBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", BSRecBracketLevel)
  2890.  
  2891.                     If (Not WithinBounds(EESBSBracket, CurrValue))
  2892.                     {
  2893.                         EESBracket := LookupRemainingAffixBracket("data\EvasionAndEnergyShield.txt", ItemLevel, CurrValue, EESBSBracket)
  2894.                        
  2895.                         If (Not IsValidBracket(EESBracket))
  2896.                         {
  2897.                             EESBracket := LookupAffixBracket("data\HybridDefences_StunRecovery.txt", ItemLevel, CurrValue)
  2898.                         }
  2899.                         If (Not WithinBounds(EESBracket, CurrValue))
  2900.                         {
  2901.                             ValueRange := AddRange(EESBSBracket, EESBracket)
  2902.                             ValueRange := MarkAsGuesstimate(ValueRange)
  2903.                             AffixType := "Comp. Prefix+Prefix"
  2904.                             NumPrefixes += 1
  2905.                         }
  2906.                     }
  2907.  
  2908.                     If (WithinBounds(BSRecPartial, BSRecValue))
  2909.                     {
  2910.                         ; BS Recovery value within bounds, this means BS Rec is all acounted for
  2911.                         BSRecPartial =
  2912.                     }
  2913.                 }
  2914.             }
  2915.             NumPrefixes += 1
  2916.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  2917.             Continue
  2918.         }
  2919.         IfInString, A_LoopField, increased Armour
  2920.         {
  2921.             AffixType := "Prefix"
  2922.             IABracketLevel := 0
  2923.             If (ItemBaseType == "Item")
  2924.             {
  2925.                 ; Global
  2926.                 PrefixPath := "data\IncrArmour_Items.txt"
  2927.                 PrefixPathOther := "data\IncrArmour_WeaponsAndArmour.txt"
  2928.             }
  2929.             Else
  2930.             {
  2931.                 ; Local
  2932.                 PrefixPath := "data\IncrArmour_WeaponsAndArmour.txt"
  2933.                 PrefixPathOther := "data\IncrArmour_Items.txt"
  2934.             }
  2935.             ValueRange := LookupAffixData(PrefixPath, ItemLevel, CurrValue, IABracketLevel, CurrTier)
  2936.             If (Not IsValidRange(ValueRange))
  2937.             {
  2938.                 ValueRange := LookupAffixData(PrefixPathOther, ItemLevel, CurrValue, IABracketLevel, CurrTier)
  2939.             }
  2940.             If (HasStunRecovery)
  2941.             {
  2942.                 IABracketLevel2 := IABracketLevel
  2943.  
  2944.                 ASRBracket := LookupAffixBracket("data\Armour_StunRecovery.txt", ItemLevel, CurrValue, IABracketLevel2)
  2945.                 If (Not IsValidRange(ValueRange) and IsValidBracket(ASRBracket))
  2946.                 {
  2947.                     ValueRange := LookupAffixData("data\Armour_StunRecovery.txt", ItemLevel, CurrValue, IABracketLevel2, CurrTier)
  2948.                 }
  2949.  
  2950.                 AffixType := "Comp. Prefix"
  2951.                 BSRecBracketLevel := 0
  2952.                 BSRecValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Stun Recovery")
  2953.                 BSRecPartial := LookupAffixBracket("data\StunRecovery_Armour.txt", IABracketLevel2, "", BSRecBracketLevel)
  2954.                 If (Not IsValidBracket(BSRecPartial) or Not IsValidBracket(ASRBracket))
  2955.                 {
  2956.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Armour.txt", IABracketLevel, "", BSRecBracketLevel)
  2957.                 }
  2958.                 If (Not IsValidBracket(BSRecPartial))
  2959.                 {
  2960.                     BSRecBracketLevel := 0
  2961.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Armour.txt", ItemLevel, BSRecValue, BSRecBracketLevel)            
  2962.                     If (Not IsValidBracket(BSRecPartial))
  2963.                     {
  2964.                         BSRecPartial := LookupAffixBracket("data\StunRecovery_Armour.txt", ItemLevel, "", BSRecBracketLevel)
  2965.                     }
  2966.  
  2967.                     IABSBracket := LookupAffixBracket("data\Armour_StunRecovery.txt", BSRecBracketLevel)
  2968.  
  2969.                     If (Not WithinBounds(IABSBracket, CurrValue))
  2970.                     {
  2971.                         IABracket := LookupRemainingAffixBracket(PrefixPath, ItemLevel, CurrValue, IABSBracket)
  2972.                         If (Not IsValidBracket(IABracket))
  2973.                         {
  2974.                             IABracket := LookupAffixBracket(PrefixPath, ItemLevel, CurrValue)
  2975.                         }
  2976.                         If (Not WithinBounds(IABracket, CurrValue))
  2977.                         {
  2978.                             ValueRange := AddRange(IABSBracket, IABracket)
  2979.                             ValueRange := MarkAsGuesstimate(ValueRange)
  2980.                             AffixType := "Comp. Prefix+Prefix"
  2981.                             NumPrefixes += 1
  2982.                         }
  2983.                     }
  2984.  
  2985.                     If (WithinBounds(BSRecPartial, BSRecValue))
  2986.                     {
  2987.                         ; BS Recovery value within bounds, this means BS Rec is all acounted for
  2988.                         BSRecPartial =
  2989.                     }
  2990.                 }
  2991.             }
  2992.             NumPrefixes += 1
  2993.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  2994.             Continue
  2995.         }
  2996.         IfInString, A_LoopField, to Evasion Rating
  2997.         {
  2998.             NumPrefixes += 1
  2999.             If (ItemBaseType == "Item")
  3000.             {
  3001.                 ValueRange := LookupAffixData("data\ToEvasion_Items.txt", ItemLevel, CurrValue, "", CurrTier)
  3002.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3003.             }
  3004.             Else
  3005.             {
  3006.                 ValueRange := LookupAffixData("data\ToEvasion_Armour.txt", ItemLevel, CurrValue, "", CurrTier)
  3007.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3008.             }
  3009.             Continue
  3010.         }
  3011.         IfInString, A_LoopField, increased Evasion Rating
  3012.         {
  3013.             AffixType := "Prefix"
  3014.             IEBracketLevel := 0
  3015.             If (ItemBaseType == "Item")
  3016.             {
  3017.                 ; Global
  3018.                 PrefixPath := "data\IncrEvasion_Items.txt"
  3019.                 PrefixPathOther := "data\IncrEvasion_Armour.txt"
  3020.             }
  3021.             Else
  3022.             {
  3023.                 ; Local
  3024.                 PrefixPath := "data\IncrEvasion_Armour.txt"
  3025.                 PrefixPathOther := "data\IncrEvasion_Items.txt"
  3026.             }
  3027.             ValueRange := LookupAffixData(PrefixPath, ItemLevel, CurrValue, IEBracketLevel, CurrTier)
  3028.             If (Not IsValidRange(ValueRange))
  3029.             {
  3030.                 ValueRange := LookupAffixData(PrefixPathOther, ItemLevel, CurrValue, IEBracketLevel, CurrTier)
  3031.             }
  3032.             If (HasStunRecovery)
  3033.             {
  3034.                 IEBracketLevel2 := IEBracketLevel
  3035.  
  3036.                 ; Determine composite bracket level and store in IEBracketLevel2, for example:
  3037.                 ;   8% increased Evasion
  3038.                 ;   26% increased Stun Recovery
  3039.                 ;   => 8% is bracket level 2 (6-14), so 'B&S Recovery from Evasion' level 2 makes
  3040.                 ;      BSRec partial 6-7
  3041.                 ERSRBracket := LookupAffixBracket("data\Evasion_StunRecovery.txt", ItemLevel, CurrValue, IEBracketLevel2)
  3042.                 If (Not IsValidRange(ValueRange) and IsValidBracket(ERSRBracket))
  3043.                 {
  3044.                     ValueRange := LookupAffixData("data\Evasion_StunRecovery.txt", ItemLevel, CurrValue, IEBracketLevel2, CurrTier)
  3045.                 }
  3046.  
  3047.                 AffixType := "Comp. Prefix"
  3048.                 BSRecBracketLevel := 0
  3049.                 BSRecValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Stun Recovery")
  3050.                 BSRecPartial := LookupAffixBracket("data\StunRecovery_Evasion.txt", IEBracketLevel2, "", BSRecBracketLevel)
  3051.                 If (Not IsValidBracket(BSRecPartial) or Not IsValidBracket(ERSRBracket))
  3052.                 {
  3053.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Evasion.txt", IEBracketLevel, "", BSRecBracketLevel)
  3054.                 }
  3055.                 If (Not IsValidRange(ValueRange) and (Not IsValidBracket(BSRecPartial) or Not WithinBounds(BSRecPartial, BSRecValue)))
  3056.                 {
  3057.                     BSRecBracketLevel := 0
  3058.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_Evasion.txt", ItemLevel, BSRecValue, BSRecBracketLevel)
  3059.                     If (Not IsValidBracket(BSRecPartial))
  3060.                     {
  3061.                         BSRecPartial := LookupAffixBracket("data\StunRecovery_Evasion.txt", ItemLevel, "", BSRecBracketLevel)
  3062.                     }
  3063.                    
  3064.                     IEBSBracket := LookupAffixBracket("data\Evasion_StunRecovery.txt", BSRecBracketLevel)
  3065.  
  3066.                     If (Not WithinBounds(IEBSBracket, CurrValue))
  3067.                     {
  3068.                         IEBracket := LookupRemainingAffixBracket(PrefixPath, ItemLevel, CurrValue, IEBSBracket)
  3069.                         If (Not IsValidBracket(IEBracket))
  3070.                         {
  3071.                             IEBracket := LookupAffixBracket(PrefixPath, ItemLevel, CurrValue, "")
  3072.                         }
  3073.                         If (Not WithinBounds(IEBracket, CurrValue))
  3074.                         {
  3075.                             ValueRange := AddRange(IEBSBracket, IEBracket)
  3076.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3077.                             AffixType := "Comp. Prefix+Prefix"
  3078.                             NumPrefixes += 1
  3079.                         }
  3080.                     }
  3081.  
  3082.                     If (WithinBounds(BSRecPartial, BSRecValue))
  3083.                     {
  3084.                         ; BS Recovery value within bounds, this means BS Rec is all acounted for
  3085.                         BSRecPartial =
  3086.                     }
  3087.                 }
  3088.             }
  3089.             NumPrefixes += 1
  3090.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3091.             Continue
  3092.         }
  3093.         IfInString, A_LoopField, to maximum Energy Shield
  3094.         {
  3095.             PrefixType := "Prefix"
  3096.             If (ItemSubType == "Ring" or ItemSubType == "Amulet" or ItemSubType == "Belt")
  3097.             {
  3098.                 ValueRange := LookupAffixData("data\ToMaxEnergyShield.txt", ItemLevel, CurrValue, "", CurrTier)
  3099.             }
  3100.             Else
  3101.             {
  3102.                 ValueRange := LookupAffixData("data\ToEnergyShield.txt", ItemLevel, CurrValue, "", CurrTier)
  3103.  
  3104.             }
  3105.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3106.             NumPrefixes += 1
  3107.             Continue
  3108.         }
  3109.         IfInString, A_LoopField, increased Energy Shield
  3110.         {
  3111.             AffixType := "Prefix"
  3112.             IESBracketLevel := 0
  3113.             PrefixPath := "data\IncrEnergyShield.txt"
  3114.             ValueRange := LookupAffixData(PrefixPath, ItemLevel, CurrValue, IESBracketLevel, CurrTier)
  3115.  
  3116.             If (HasStunRecovery)
  3117.             {
  3118.                 IESBracketLevel2 := IESBracketLevel
  3119.  
  3120.                 ESSRBracket := LookupAffixBracket("data\EnergyShield_StunRecovery.txt", ItemLevel, CurrValue, IESBracketLevel2)
  3121.                 If (Not IsValidRange(ValueRange) and IsValidBracket(ESSRBracket))
  3122.                 {
  3123.                     ValueRange := LookupAffixData("data\EnergyShield_StunRecovery.txt", ItemLevel, CurrValue, IESBracketLevel2, CurrTier)
  3124.                 }
  3125.  
  3126.                 AffixType := "Comp. Prefix"
  3127.                 BSRecBracketLevel := 0
  3128.                 BSRecPartial := LookupAffixBracket("data\StunRecovery_EnergyShield.txt", IESBracketLevel2, "", BSRecBracketLevel)
  3129.                 BSRecValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Stun Recovery")
  3130.                 If (Not IsValidBracket(BSRecPartial) or Not IsValidBracket(ESSRBracket))
  3131.                 {
  3132.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_EnergyShield.txt", IESBracketLevel, "", BSRecBracketLevel)
  3133.                 }
  3134.                 If (Not IsValidBracket(BSRecPartial))
  3135.                 {
  3136.                     BSRecBracketLevel := 0
  3137.                     BSRecPartial := LookupAffixBracket("data\StunRecovery_EnergyShield.txt", ItemLevel, BSRecValue, BSRecBracketLevel)
  3138.                     If (Not IsValidBracket(BSRecPartial))
  3139.                     {
  3140.                         BSRecPartial := LookupAffixBracket("data\StunRecovery_EnergyShield.txt", ItemLevel, "", BSRecBracketLevel)
  3141.                     }
  3142.                     IESBSBracket := LookupAffixBracket("data\EnergyShield_StunRecovery.txt", BSRecBracketLevel)
  3143.  
  3144.                     If (Not WithinBounds(IEBSBracket, CurrValue))
  3145.                     {                    
  3146.                         IESBracket := LookupRemainingAffixBracket(PrefixPath, ItemLevel, CurrValue, IESBSBracket)
  3147.  
  3148.                         If (Not WithinBounds(IESBracket, CurrValue))
  3149.                         {
  3150.                             ValueRange := AddRange(IESBSBracket, IESBracket)
  3151.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3152.                             AffixType := "Comp. Prefix+Prefix"
  3153.                             NumPrefixes += 1
  3154.                         }
  3155.                     }
  3156.  
  3157.                     If (WithinBounds(BSRecPartial, BSRecValue))
  3158.                     {
  3159.                         ; BS Recovery value within bounds, this means BS Rec is all acounted for
  3160.                         BSRecPartial =
  3161.                     }
  3162.                 }
  3163.             }
  3164.             NumPrefixes += 1
  3165.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3166.             Continue
  3167.         }
  3168.         IfInString, A_LoopField, increased maximum Energy Shield
  3169.         {
  3170.             NumPrefixes += 1
  3171.             ValueRange := LookupAffixData("data\IncrMaxEnergyShield_Amulets.txt", ItemLevel, CurrValue, "", CurrTier)
  3172.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3173.             Continue
  3174.         }
  3175.         If RegExMatch(A_LoopField, "Adds \d+?\-\d+? Physical Damage")
  3176.         {
  3177.             If (ItemBaseType == "Weapon")
  3178.             {
  3179.                 If (ItemSubType == "Bow")
  3180.                 {
  3181.                     ValueRange := LookupAffixData("data\AddedPhysDamage_2H.txt", ItemLevel, CurrValue, "", CurrTier)
  3182.                 }
  3183.                 Else
  3184.                 {
  3185.                     If (ItemGripType == "1H") ; One handed weapons
  3186.                     {
  3187.                         ValueRange := LookupAffixData("data\AddedPhysDamage_1H.txt", ItemLevel, CurrValue, "", CurrTier)
  3188.                     }
  3189.                     Else
  3190.                     {
  3191.                         ValueRange := LookupAffixData("data\AddedPhysDamage_2H.txt", ItemLevel, CurrValue, "", CurrTier)
  3192.                     }
  3193.                 }
  3194.             }
  3195.             Else
  3196.             {
  3197.                 If (ItemSubType == "Amulet")
  3198.                 {
  3199.                     ValueRange := LookupAffixData("data\AddedPhysDamage_Amulets.txt", ItemLevel, CurrValue, "", CurrTier)
  3200.                 }
  3201.                 Else
  3202.                 {
  3203.                     If (ItemSubType == "Quiver")
  3204.                     {
  3205.                         ValueRange := LookupAffixData("data\AddedPhysDamage_Quivers.txt", ItemLevel, CurrValue, "", CurrTier)
  3206.                     }
  3207.                     Else
  3208.                     {
  3209.                         If (ItemSubType == "Ring")
  3210.                         {
  3211.                             ValueRange := LookupAffixData("data\AddedPhysDamage_Rings.txt", ItemLevel, CurrValue, "", CurrTier)
  3212.                         }
  3213.                         Else
  3214.                         {
  3215.                             ; There is no Else for rare items, but some uniques have added phys damage.
  3216.                             ; Just lookup in 1H for now...
  3217.                             ValueRange := LookupAffixData("data\AddedPhysDamage_Amulets.txt", ItemLevel, CurrValue, "", CurrTier)
  3218.                         }
  3219.                     }
  3220.                 }
  3221.             }
  3222.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3223.             NumPrefixes += 1
  3224.             Continue
  3225.         }
  3226.         If RegExMatch(A_LoopField, "Adds \d+?\-\d+? Cold Damage")
  3227.         {
  3228.             If (ItemSubType == "Amulet" or ItemSubType == "Ring")
  3229.             {
  3230.                 ValueRange := LookupAffixData("data\AddedColdDamage_RingsAndAmulets.txt", ItemLevel, CurrValue, "", CurrTier)
  3231.             }
  3232.             Else
  3233.             {
  3234.                 If (ItemSubType == "Gloves")
  3235.                 {
  3236.                     ValueRange := LookupAffixData("data\AddedColdDamage_Gloves.txt", ItemLevel, CurrValue, "", CurrTier)
  3237.                 }
  3238.                 Else
  3239.                 {
  3240.                     If (ItemSubType == "Quiver")
  3241.                     {
  3242.                         ValueRange := LookupAffixData("data\AddedColdDamage_Quivers.txt", ItemLevel, CurrValue, "", CurrTier)
  3243.                     }
  3244.                     Else
  3245.                     {
  3246.                         If (ItemGripType == "1H")
  3247.                         {
  3248.                             ValueRange := LookupAffixData("data\AddedColdDamage_1H.txt", ItemLevel, CurrValue, "", CurrTier)
  3249.                         }
  3250.                         Else
  3251.                         {
  3252.                             ValueRange := LookupAffixData("data\AddedColdDamage_2H.txt", ItemLevel, CurrValue, "", CurrTier)
  3253.                         }
  3254.                     }
  3255.                 }
  3256.             }
  3257.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3258.             NumPrefixes += 1
  3259.             Continue
  3260.         }
  3261.         If RegExMatch(A_LoopField, "Adds \d+?\-\d+? Fire Damage")
  3262.         {
  3263.             If (ItemSubType == "Amulet" or ItemSubType == "Ring")
  3264.             {
  3265.                 ValueRange := LookupAffixData("data\AddedFireDamage_RingsAndAmulets.txt", ItemLevel, CurrValue, "", CurrTier)
  3266.             }
  3267.             Else
  3268.             {
  3269.                 If (ItemSubType == "Gloves")
  3270.                 {
  3271.                     ValueRange := LookupAffixData("data\AddedFireDamage_Gloves.txt", ItemLevel, CurrValue, "", CurrTier)
  3272.                 }
  3273.                 Else
  3274.                 {
  3275.                     If (ItemSubType == "Quiver")
  3276.                     {
  3277.                         ValueRange := LookupAffixData("data\AddedFireDamage_Quivers.txt", ItemLevel, CurrValue, "", CurrTier)
  3278.                     }
  3279.                     Else
  3280.                     {
  3281.                         If (ItemGripType == "1H") ; One handed weapons
  3282.                         {
  3283.                             ValueRange := LookupAffixData("data\AddedFireDamage_1H.txt", ItemLevel, CurrValue, "", CurrTier)
  3284.                         }
  3285.                         Else
  3286.                         {
  3287.                             ValueRange := LookupAffixData("data\AddedFireDamage_2H.txt", ItemLevel, CurrValue, "", CurrTier)
  3288.                         }
  3289.                     }
  3290.                 }
  3291.             }
  3292.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3293.             NumPrefixes += 1
  3294.             Continue
  3295.         }
  3296.         If RegExMatch(A_LoopField, "Adds \d+?\-\d+? Lightning Damage")
  3297.         {
  3298.             If (ItemSubType == "Amulet" or ItemSubType == "Ring")
  3299.             {
  3300.                 ValueRange := LookupAffixData("data\AddedLightningDamage_RingsAndAmulets.txt", ItemLevel, CurrValue, "", CurrTier)
  3301.             }
  3302.             Else
  3303.             {
  3304.                 If (ItemSubType == "Gloves")
  3305.                 {
  3306.                     ValueRange := LookupAffixData("data\AddedLightningDamage_Gloves.txt", ItemLevel, CurrValue, "", CurrTier)
  3307.                 }
  3308.                 Else
  3309.                 {
  3310.                     If (ItemSubType == "Quiver")
  3311.                     {
  3312.                         ValueRange := LookupAffixData("data\AddedLightningDamage_Quivers.txt", ItemLevel, CurrValue, "", CurrTier)
  3313.                     }
  3314.                     Else
  3315.                     {
  3316.                         If (ItemGripType == "1H") ; One handed weapons
  3317.                         {
  3318.                             ValueRange := LookupAffixData("data\AddedLightningDamage_1H.txt", ItemLevel, CurrValue, "", CurrTier)
  3319.                         }
  3320.                         Else
  3321.                         {
  3322.                             ValueRange := LookupAffixData("data\AddedLightningDamage_2H.txt", ItemLevel, CurrValue, "", CurrTier)
  3323.                         }
  3324.                     }
  3325.                 }
  3326.             }
  3327.             ActualRange := GetActualValue(A_LoopField)
  3328.             AffixType := "Prefix"
  3329.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3330.             NumPrefixes += 1
  3331.             Continue
  3332.         }
  3333.         IfInString, A_LoopField, Physical Damage to Melee Attackers
  3334.         {
  3335.             NumPrefixes += 1
  3336.             ValueRange := LookupAffixData("data\PhysDamagereturn.txt", ItemLevel, CurrValue, "", CurrTier)
  3337.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3338.             Continue
  3339.         }
  3340.         IfInString, A_LoopField, to Level of Socketed
  3341.         {
  3342.             If (ItemBaseType == "Weapon")
  3343.             {
  3344.                 If (ItemSubType == "Bow")
  3345.                 {
  3346.                     ValueRange := LookupAffixData("data\GemLevel_Bow.txt", ItemLevel, CurrValue, "", CurrTier)
  3347.                 }
  3348.                 Else
  3349.                 {
  3350.                     If (InStr(A_LoopField, "Fire") or InStr(A_LoopField, "Cold") or InStr(A_LoopField, "Lightning"))
  3351.                     {
  3352.                         ValueRange := LookupAffixData("data\GemLevel_Elemental.txt", ItemLevel, CurrValue, "", CurrTier)
  3353.                     }
  3354.                     Else
  3355.                     {
  3356.                         If (InStr(A_LoopField, "Melee"))
  3357.                         {
  3358.                             ValueRange := LookupAffixData("data\GemLevel_Melee.txt", ItemLevel, CurrValue, "", CurrTier)
  3359.                         }
  3360.                         Else
  3361.                         {
  3362.                             ; Paragorn's
  3363.                             ValueRange := LookupAffixData("data\GemLevel.txt", ItemLevel, CurrValue, "", CurrTier)
  3364.                         }
  3365.                     }
  3366.                 }
  3367.             }
  3368.             Else
  3369.             {
  3370.                 If (InStr(A_LoopField, "Minion"))
  3371.                 {
  3372.                     ValueRange := LookupAffixData("data\GemLevel_Minion.txt", ItemLevel, CurrValue, "", CurrTier)
  3373.                 }
  3374.                 Else If (InStr(A_LoopField, "Fire") or InStr(A_LoopField, "Cold") or InStr(A_LoopField, "Lightning"))
  3375.                 {
  3376.                     ValueRange := LookupAffixData("data\GemLevel_Elemental.txt", ItemLevel, CurrValue, "", CurrTier)
  3377.                 }
  3378.                 Else If (InStr(A_LoopField, "Melee"))
  3379.                 {
  3380.                     ValueRange := LookupAffixData("data\GemLevel_Melee.txt", ItemLevel, CurrValue, "", CurrTier)
  3381.                 }
  3382.             }
  3383.             NumPrefixes += 1
  3384.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3385.             Continue
  3386.         }
  3387.         IfInString, A_LoopField, maximum Life
  3388.         {
  3389.             ValueRange := LookupAffixData("data\MaxLife.txt", ItemLevel, CurrValue, "", CurrTier)
  3390.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3391.             NumPrefixes += 1
  3392.             Continue
  3393.         }
  3394.         IfInString, A_LoopField, Physical Attack Damage Leeched as
  3395.         {
  3396.             NumPrefixes += 1
  3397.             ValueRange := LookupAffixData("data\LifeLeech.txt", ItemLevel, CurrValue, "", CurrTier)
  3398.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3399.             Continue
  3400.         }
  3401.         IfInString, A_LoopField, Movement Speed
  3402.         {
  3403.             NumPrefixes += 1
  3404.             ValueRange := LookupAffixData("data\MovementSpeed.txt", ItemLevel, CurrValue, "", CurrTier)
  3405.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3406.             Continue
  3407.         }
  3408.         IfInString, A_LoopField, increased Elemental Damage with Weapons
  3409.         {
  3410.             NumPrefixes += 1
  3411.             ValueRange := LookupAffixData("data\IncrWeaponElementalDamage.txt", ItemLevel, CurrValue, "", CurrTier)
  3412.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3413.             Continue
  3414.         }
  3415.  
  3416.         ; Flask effects (on belts)
  3417.         IfInString, A_LoopField, increased Flask Mana Recovery rate
  3418.         {
  3419.             NumPrefixes += 1
  3420.             ValueRange := LookupAffixData("data\FlaskManaRecoveryRate.txt", ItemLevel, CurrValue, "", CurrTier)
  3421.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3422.             Continue
  3423.         }
  3424.         IfInString, A_LoopField, increased Flask Life Recovery rate
  3425.         {
  3426.             NumPrefixes += 1
  3427.             ValueRange := LookupAffixData("data\FlaskLifeRecoveryRate.txt", ItemLevel, CurrValue, "", CurrTier)
  3428.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  3429.             Continue
  3430.         }
  3431.     }
  3432.  
  3433.     ; --- COMPLEX AFFIXES ---
  3434.  
  3435.     Loop, Parse, ItemDataChunk, `n, `r
  3436.     {
  3437.         If StrLen(A_LoopField) = 0
  3438.         {
  3439.             Break ; Not interested in blank lines
  3440.         }
  3441.         IfInString, ItemDataChunk, Unidentified
  3442.         {
  3443.             Break ; Not interested in unidentified items
  3444.         }
  3445.  
  3446.         CurrValue := GetActualValue(A_LoopField)
  3447.  
  3448.         ; "Spell Damage +%" (simple prefix)
  3449.         ; "Spell Damage +% (1H)" / "Base Maximum Mana" - Limited to sceptres, wands, and daggers.
  3450.         ; "Spell Damage +% (Staff)" / "Base Maximum Mana"
  3451.         IfInString, A_LoopField, increased Spell Damage
  3452.         {
  3453.             AffixType := "Prefix"
  3454.             If (HasMaxMana)
  3455.             {
  3456.                 SDBracketLevel := 0
  3457.                 MMBracketLevel := 0
  3458.                 MaxManaValue := ExtractValueFromAffixLine(ItemDataChunk, "maximum Mana")
  3459.                 If (ItemSubType == "Staff")
  3460.                 {
  3461.                     SpellDamageBracket := LookupAffixBracket("data\SpellDamage_MaxMana_Staff.txt", ItemLevel, CurrValue, SDBracketLevel)
  3462.                     If (Not IsValidBracket(SpellDamageBracket))
  3463.                     {
  3464.                         AffixType := "Comp. Prefix+Prefix"
  3465.                         NumPrefixes += 1
  3466.                        
  3467.                         ; Need to find the bracket level by looking at max mana value instead
  3468.                         MaxManaBracket := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", ItemLevel, MaxManaValue, MMBracketLevel)
  3469.                         If (Not IsValidBracket(MaxManaBracket))
  3470.                         {
  3471.                             ; This actually means that both the "increased Spell Damage" line and
  3472.                             ; the "to maximum Mana" line are made up of Composite Prefix + Prefix.
  3473.                             ;
  3474.                             ; I haven't seen such an item yet, but you never know. In any case this
  3475.                             ; is completely ambiguous and can't be resolved. Mark line with EstInd
  3476.                             ; so user knows she needs to take a look at it.
  3477.                             AffixType := "Comp. Prefix+Comp. Prefix"
  3478.                             ValueRange := StrPad(EstInd, Opts.ValueRangeFieldWidth + StrLen(EstInd), "left")
  3479.                         }
  3480.                         Else
  3481.                         {
  3482.                             SpellDamageBracketFromComp := LookupAffixBracket("data\SpellDamage_MaxMana_Staff.txt", MMBracketLevel)
  3483.                             SpellDamageBracket := LookupRemainingAffixBracket("data\SpellDamage_Staff.txt", ItemLevel, CurrValue, SpellDamageBracketFromComp, SDBracketLevel)
  3484.                             ValueRange := AddRange(SpellDamageBracket, SpellDamageBracketFromComp)
  3485.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3486.                         }
  3487.                     }
  3488.                     Else
  3489.                     {
  3490.                         ValueRange := LookupAffixData("data\SpellDamage_MaxMana_Staff.txt", ItemLevel, CurrValue, BracketLevel, CurrTier)
  3491.                         MaxManaBracket := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", BracketLevel)
  3492.                         AffixType := "Comp. Prefix"
  3493.                     }
  3494.                 }
  3495.                 Else
  3496.                 {
  3497.                     SpellDamageBracket := LookupAffixBracket("data\SpellDamage_MaxMana_1H.txt", ItemLevel, CurrValue, SDBracketLevel)
  3498.                     If (Not IsValidBracket(SpellDamageBracket))
  3499.                     {
  3500.                         AffixType := "Comp. Prefix+Prefix"
  3501.                         NumPrefixes += 1
  3502.                        
  3503.                         ; Need to find the bracket level by looking at max mana value instead
  3504.                         MaxManaBracket := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", ItemLevel, MaxManaValue, MMBracketLevel)
  3505.                         If (Not IsValidBracket(MaxManaBracket))
  3506.                         {
  3507.                             MaxManaBracket := LookupAffixBracket("data\MaxMana.txt", ItemLevel, MaxManaValue, MMBracketLevel)
  3508.                             If (IsValidBracket(MaxManaBracket))
  3509.                             {
  3510.                                 AffixType := "Prefix"
  3511.                                 If (ItemSubType == "Staff")
  3512.                                 {
  3513.                                     ValueRange := LookupAffixData("data\SpellDamage_Staff.txt", ItemLevel, CurrValue, SDBracketLevel, CurrTier)
  3514.                                 }
  3515.                                 Else
  3516.                                 {
  3517.                                     ValueRange := LookupAffixData("data\SpellDamage_1H.txt", ItemLevel, CurrValue, SDBracketLevel, CurrTier)
  3518.                                 }
  3519.                                 ValueRange := StrPad(ValueRange, Opts.ValueRangeFieldWidth, "left")
  3520.                             }
  3521.                             Else
  3522.                             {
  3523.                                 ; Must be 1H Spell Damage and Max Mana + 1H Spell Damage (+ Max Mana)
  3524.                                 SD1HBracketLevel := 0
  3525.                                 SpellDamage1HBracket := LookupAffixBracket("data\SpellDamage_1H.txt", ItemLevel, "", SD1HBracketLevel)
  3526.                                 If (IsValidBracket(SpellDamage1HBracket))
  3527.                                 {
  3528.                                     SpellDamageBracket := LookupRemainingAffixBracket("data\SpellDamage_MaxMana_1H.txt", ItemLevel, CurrValue, SpellDamage1HBracket, SDBracketLevel)
  3529.                                     If (IsValidBracket(SpellDamageBracket))
  3530.                                     {
  3531.                                         MaxManaBracket := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", SDBracketLevel, "", MMBracketLevel)
  3532.                                        
  3533.                                         ; Check if max mana can be covered fully with the partial max mana bracket from Spell Damage Max Mana 1H
  3534.                                         MaxManaBracketRem := LookupRemainingAffixBracket("data\MaxMana.txt", ItemLevel, MaxManaValue, MaxManaBracket)
  3535.                                         If (Not IsValidBracket(MaxManaBracketRem))
  3536.                                         {
  3537.                                             ; Nope, try again: check highest spell damage max mana first then spell damage
  3538.                                             SD1HBracketLevel := 0
  3539.                                             SpellDamageBracket := LookupAffixBracket("data\SpellDamage_MaxMana_1H.txt", ItemLevel, "", SDBracketLevel)
  3540.                                             SpellDamage1HBracket := LookupRemainingAffixBracket("data\SpellDamage_1H.txt", ItemLevel, CurrValue, SpellDamageBracket, SD1HBracketLevel)
  3541.                                             MaxManaBracket := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", SDBracketLevel, "", MMBracketLevel)
  3542.                                             ; Check if max mana can be covered fully with the partial max mana bracket from Spell Damage Max Mana 1H
  3543.                                             MaxManaBracketRem := LookupRemainingAffixBracket("data\MaxMana.txt", ItemLevel, MaxManaValue, MaxManaBracket)
  3544.                                             ValueRange := AddRange(SpellDamageBracket, SpellDamage1HBracket)
  3545.                                             ValueRange := MarkAsGuesstimate(ValueRange)
  3546.                                         }
  3547.                                         Else
  3548.                                         {
  3549.                                             ValueRange := AddRange(SpellDamageBracket, SpellDamage1HBracket)
  3550.                                             ValueRange := MarkAsGuesstimate(ValueRange)
  3551.                                         }
  3552.                                     }
  3553.                                     Else
  3554.                                     {
  3555.                                         SD1HBracketLevel := 0
  3556.                                         SpellDamageBracket := LookupAffixBracket("data\SpellDamage_MaxMana_1H.txt", ItemLevel, "", SDBracketLevel)
  3557.                                         SpellDamage1HBracket := LookupRemainingAffixBracket("data\SpellDamage_1H.txt", ItemLevel, CurrValue, SpellDamageBracket, SD1HBracketLevel)
  3558.                                         MaxManaBracket := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", SDBracketLevel, "", MMBracketLevel)
  3559.                                         ; Check if max mana can be covered fully with the partial max mana bracket from Spell Damage Max Mana 1H
  3560.                                         MaxManaBracketRem := LookupRemainingAffixBracket("data\MaxMana.txt", ItemLevel, MaxManaValue, MaxManaBracket)
  3561.                                         ValueRange := AddRange(SpellDamageBracket, SpellDamage1HBracket)
  3562.                                         ValueRange := MarkAsGuesstimate(ValueRange)
  3563.                                     }
  3564.                                 }
  3565.                                 Else
  3566.                                 {
  3567.                                     ShowUnhandledCaseDialog()
  3568.                                     ValueRange := StrPad("n/a", Opts.ValueRangeFieldWidth, "left")
  3569.                                 }
  3570.                             }
  3571.                         }
  3572.                         Else
  3573.                         {
  3574.                             SpellDamageBracketFromComp := LookupAffixBracket("data\SpellDamage_MaxMana_1H.txt", MMBracketLevel)
  3575.                             SpellDamageBracket := LookupRemainingAffixBracket("data\SpellDamage_1H.txt", ItemLevel, CurrValue, SpellDamageBracketFromComp, SDBracketLevel)
  3576.                             ValueRange := AddRange(SpellDamageBracket, SpellDamageBracketFromComp)
  3577.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3578.                         }
  3579.                     }
  3580.                     Else
  3581.                     {
  3582.                         ValueRange := LookupAffixData("data\SpellDamage_MaxMana_1H.txt", ItemLevel, CurrValue, BracketLevel, CurrTier)
  3583.                         MaxManaBracket := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", BracketLevel)
  3584.                         AffixType := "Comp. Prefix"
  3585.                     }
  3586.                 }
  3587.                 ; If MaxManaValue falls within bounds of MaxManaBracket this means the max mana value is already fully accounted for
  3588.                 If (WithinBounds(MaxManaBracket, MaxManaValue))
  3589.                 {
  3590.                     MaxManaPartial =
  3591.                 }
  3592.                 Else
  3593.                 {
  3594.                     MaxManaPartial := MaxManaBracket
  3595.                 }
  3596.             }
  3597.             Else
  3598.             {
  3599.                 If (ItemSubType == "Amulet")
  3600.                 {
  3601.                     ValueRange := LookupAffixData("data\SpellDamage_Amulets.txt", ItemLevel, CurrValue, "", CurrTier)
  3602.                 }
  3603.                 Else
  3604.                 {
  3605.                     If (ItemSubType == "Staff")
  3606.                     {
  3607.                         ValueRange := LookupAffixData("data\SpellDamage_Staff.txt", ItemLevel, CurrValue, "", CurrTier)
  3608.                     }
  3609.                     Else
  3610.                     {
  3611.                         ValueRange := LookupAffixData("data\SpellDamage_1H.txt", ItemLevel, CurrValue, "", CurrTier)
  3612.                     }
  3613.                 }
  3614.                 NumPrefixes += 1
  3615.             }
  3616.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3617.             Continue
  3618.         }
  3619.  
  3620.         ; "Base Maximum Mana" (simple Prefix)
  3621.         ; "1H Spell Damage" / "Base Maximum Mana" (complex Prefix)
  3622.         ; "Staff Spell Damage" / "Base Maximum Mana" (complex Prefix)
  3623.         IfInString, A_LoopField, maximum Mana
  3624.         {
  3625.             AffixType := "Prefix"
  3626.             If (ItemBaseType == "Weapon")
  3627.             {
  3628.                 If (HasSpellDamage)
  3629.                 {
  3630.                     If (MaxManaPartial and Not WithinBounds(MaxManaPartial, CurrValue))
  3631.                     {
  3632.                         NumPrefixes += 1
  3633.                         AffixType := "Comp. Prefix+Prefix"
  3634.  
  3635.                         ValueRange := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", ItemLevel, CurrValue)
  3636.                         MaxManaRest := CurrValue-RangeMid(MaxManaPartial)
  3637.  
  3638.                         If (MaxManaRest >= 15) ; 15 because the lowest possible value at this time for Max Mana is 15 at bracket level 1
  3639.                         {
  3640.                             ; Lookup remaining Max Mana bracket that comes from Max Mana being concatenated as simple prefix
  3641.                             ValueRange1 := LookupAffixBracket("data\MaxMana.txt", ItemLevel, MaxManaRest)
  3642.                             ValueRange2 := MaxManaPartial
  3643.  
  3644.                             ; Add these ranges together to get an estimated range
  3645.                             ValueRange := AddRange(ValueRange1, ValueRange2)
  3646.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3647.                         }
  3648.                         Else
  3649.                         {
  3650.                             ; Could be that the spell damage affix is actually a pure spell damage affix
  3651.                             ; (w/o the added max mana) so this would mean max mana is a pure prefix - if
  3652.                             ; NumPrefixes allows it, ofc...
  3653.                             If (NumPrefixes < 3)
  3654.                             {
  3655.                                 AffixType := "Prefix"
  3656.                                 ValueRange := LookupAffixData("data\MaxMana.txt", ItemLevel, CurrValue, "", CurrTier)
  3657.                                 ChangeAffixDetailLine("increased Spell Damage", "Comp. Prefix", "Prefix")
  3658.                             }
  3659.                         }
  3660.                     }
  3661.                     Else
  3662.                     {
  3663.                         ; It's on a weapon, there is Spell Damage but no MaxManaPartial or NumPrefixes already is 3
  3664.                         AffixType := "Comp. Prefix"
  3665.                         ValueRange := LookupAffixBracket("data\MaxMana_SpellDamage_StaffAnd1H.txt", ItemLevel, CurrValue)
  3666.                         If (Not IsValidBracket(ValueRange))
  3667.                         {
  3668.                             ; incr. Spell Damage is actually a Prefix and not a Comp. Prefix,
  3669.                             ; so Max Mana must be a normal Prefix as well then
  3670.                             AffixType := "Prefix"
  3671.                             ValueRange := LookupAffixData("data\MaxMana.txt", ItemLevel, CurrValue, "", CurrTier)
  3672.                         }
  3673.                         Else
  3674.                         {
  3675.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3676.                         }
  3677.                     }
  3678.                     ; Check if we still need to increment for the Spell Damage part
  3679.                     If (NumPrefixes < 3)
  3680.                     {
  3681.                         NumPrefixes += 1
  3682.                     }
  3683.                 }
  3684.                 Else
  3685.                 {
  3686.                     ; It's on a weapon but there is no Spell Damage, which makes it a simple Prefix
  3687.                     Goto, SimpleMaxManaPrefix
  3688.                 }
  3689.             }
  3690.             Else
  3691.             {
  3692.                 ; Armour...
  3693.                 ; Max Mana cannot appear on belts but I won't exclude them for now
  3694.                 ; to future-proof against when max mana on belts might be added.
  3695.                 Goto, SimpleMaxManaPrefix
  3696.             }
  3697.  
  3698.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3699.             Continue
  3700.  
  3701.         SimpleMaxManaPrefix:
  3702.             NumPrefixes += 1
  3703.             ValueRange := LookupAffixData("data\MaxMana.txt", ItemLevel, CurrValue, "", CurrTier)
  3704.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3705.             Continue
  3706.         }
  3707.  
  3708.         ; "Local Physical Damage +%" (simple Prefix)
  3709.         ; "Local Physical Damage +%" / "Local Accuracy Rating" (complex Prefix)
  3710.         ; - only on Weapons
  3711.         ; - needs to come before Accuracy Rating stuff (!)
  3712.         IfInString, A_LoopField, increased Physical Damage
  3713.         {
  3714.             AffixType := "Prefix"
  3715.             IPDPath := "data\IncrPhysDamage.txt"
  3716.             If (HasToAccuracyRating)
  3717.             {
  3718.                 ARIPDPath := "data\AccuracyRating_IncrPhysDamage.txt"
  3719.                 IPDARPath := "data\IncrPhysDamage_AccuracyRating.txt"
  3720.                 ARValue := ExtractValueFromAffixLine(ItemDataChunk, "to Accuracy Rating")
  3721.                 ARPath := "data\AccuracyRating_Global.txt"
  3722.                 If (ItemBaseType == "Weapon")
  3723.                 {
  3724.                     ARPath := "data\AccuracyRating_Local.txt"
  3725.                 }
  3726.  
  3727.                 ; Look up IPD bracket, and use its bracket level to cross reference the corresponding
  3728.                 ; AR bracket. If both check out (are within bounds of their bracket level) case is
  3729.                 ; simple: Comp. Prefix (IPD / AR)
  3730.                 IPDBracketLevel := 0
  3731.                 IPDBracket := LookupAffixBracket(IPDARPath, ItemLevel, CurrValue, IPDBracketLevel)
  3732.                 ARBracket := LookupAffixBracket(ARIPDPath, IPDBracketLevel)
  3733.                
  3734.                 If (HasIncrLightRadius)
  3735.                 {
  3736.                     LRValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Light Radius")
  3737.                     ; First check if the AR value that comes with the Comp. Prefix AR / Light Radius
  3738.                     ; already covers the complete AR value. If so, from that follows that the Incr.
  3739.                     ; Phys Damage value can only be a Damage Scaling prefix.
  3740.                     LRBracketLevel := 0
  3741.                     LRBracket := LookupAffixBracket("data\LightRadius_AccuracyRating.txt", ItemLevel, LRValue, LRBracketLevel)
  3742.                     ARLRBracket := LookupAffixBracket("data\AccuracyRating_LightRadius.txt", LRBracketLevel)
  3743.                     If (IsValidBracket(ARLRBracket))
  3744.                     {
  3745.                         If (WithinBounds(ARLRBracket, ARValue) and WithinBounds(IPDBracket, CurrValue))
  3746.                         {
  3747.                             Goto, SimpleIPDPrefix
  3748.                         }
  3749.                     }
  3750.                 }
  3751.  
  3752.                 If (IsValidBracket(IPDBracket) and IsValidBracket(ARBracket))
  3753.                 {
  3754.                     Goto, CompIPDARPrefix
  3755.                 }
  3756.  
  3757.                 If (Not IsValidBracket(IPDBracket))
  3758.                 {
  3759.                     IPDBracket := LookupAffixBracket(IPDPath, ItemLevel, CurrValue)
  3760.                     ARBracket := LookupAffixBracket(ARPath, ItemLevel, ARValue)  ; Also lookup AR as if it were a simple Suffix
  3761.                     ARIPDBracket := LookupAffixBracket(ARIPDPath, ItemLevel, ARValue, ARBracketLevel)
  3762.  
  3763.                     If (IsValidBracket(IPDBracket) and IsValidBracket(ARBracket) and NumPrefixes < 3)
  3764.                     {
  3765.                         HasIncrPhysDmg := 0
  3766.                         Goto, SimpleIPDPrefix
  3767.                     }
  3768.                     ARBracketLevel := 0
  3769.                     ARBracket := LookupAffixBracket(ARIPDPath, ItemLevel, ARValue, ARBracketLevel)
  3770.                     If (IsValidBracket(ARBracket))
  3771.                     {
  3772.                         IPDARBracket := LookupAffixBracket(IPDARPath, ARBracketLevel)
  3773.                         IPDBracket := LookupRemainingAffixBracket(IPDPath, ItemLevel, CurrValue, IPDARBracket)
  3774.                         If (IsValidBracket(IPDBracket))
  3775.                         {
  3776.                             ValueRange := AddRange(IPDARBracket, IPDBracket)
  3777.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3778.                             ARAffixTypePartial := "Comp. Prefix"
  3779.                             Goto, CompIPDARPrefixPrefix
  3780.                         }
  3781.                     }
  3782.                     If (Not IsValidBracket(IPDBracket) and IsValidBracket(ARBracket))
  3783.                     {
  3784.                         If (Not WithinBounds(ARBracket, ARValue))
  3785.                         {
  3786.                             ARRest := ARValue - RangeMid(ARBracket)
  3787.                         }
  3788.                         IPDBracket := LookupRemainingAffixBracket(IPDPath, ItemLevel, CurrValue, IPDARBracket, IPDBracketLevel)
  3789.                         If (IsValidBracket(IPDBracket))
  3790.                         {
  3791.                             ValueRange := AddRange(IPDARBracket, IPDBracket)
  3792.                             ValueRange := MarkAsGuesstimate(ValueRange)
  3793.                             ARAffixTypePartial := "Comp. Prefix"
  3794.                             Goto, CompIPDARPrefixPrefix
  3795.                         }
  3796.                         Else If (IsValidBracket(IPDARBracket) and NumPrefixes < 3)
  3797.                         {
  3798.                             IPDBracket := LookupRemainingAffixBracket(IPDPath, ItemLevel, IPDRest, IPDARBracket)
  3799.                             If (IsValidBracket(IPDBracket))
  3800.                             {
  3801.                                 NumPrefixes += 1
  3802.                                 ValueRange := AddRange(IPDARBracket, IPDBracket)
  3803.                                 ValueRange := MarkAsGuesstimate(ValueRange)
  3804.                                 ARAffixTypePartial := "Comp. Prefix"
  3805.                                 Goto, CompIPDARPrefixPrefix
  3806.                             }
  3807.  
  3808.                         }
  3809.                     }
  3810.                     If ((Not IsValidBracket(IPDBracket)) and (Not IsValidBracket(ARBracket)))
  3811.                     {
  3812.                         IPDBracket := LookupAffixBracket(IPDPath, ItemLevel, "")
  3813.                         IPDARBracket := LookupRemainingAffixBracket(IPDARPath, ItemLevel, CurrValue, IPDBracket, ARBracketLevel)
  3814.                         ARBracket := LookupAffixBracket(ARIPDPath, ARBracketLevel, "")
  3815.                         ValueRange := AddRange(IPDARBracket, IPDBracket)
  3816.                         ValueRange := MarkAsGuesstimate(ValueRange)
  3817.                         Goto, CompIPDARPrefixPrefix
  3818.                     }
  3819.                 }
  3820.  
  3821.                 If ((Not IsValidBracket(IPDBracket)) and (Not IsValidBracket(ARBracket)))
  3822.                 {
  3823.                     HasIncrPhysDmg := 0
  3824.                     Goto, CompIPDARPrefixPrefix
  3825.                 }
  3826.  
  3827.                 If (IsValidBracket(ARBracket))
  3828.                 {
  3829.                     ; AR bracket not found in the composite IPD/AR table
  3830.                     ARValue := ExtractValueFromAffixLine(ItemDataChunk, "to Accuracy Rating")
  3831.                     ARBracket := LookupAffixBracket(ARPath, ItemLevel, ARValue)
  3832.  
  3833.                     Goto, CompIPDARPrefix
  3834.                 }
  3835.                 If (IsValidBracket(IPDBracket))
  3836.                 {
  3837.                     ; AR bracket was found in the comp. IPD/AR table, but not the IPD bracket
  3838.                     Goto, SimpleIPDPrefix
  3839.                 }
  3840.                 Else
  3841.                 {
  3842.                     ValueRange := LookupAffixData(IPDPath, ItemLevel, CurrValue, "", CurrTier)
  3843.                 }
  3844.             }
  3845.             Else
  3846.             {
  3847.                 Goto, SimpleIPDPrefix
  3848.             }
  3849.            
  3850.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3851.             Continue
  3852.  
  3853.        SimpleIPDPrefix:
  3854.             NumPrefixes += 1
  3855.             ValueRange := LookupAffixData("data\IncrPhysDamage.txt", ItemLevel, CurrValue, "", CurrTier)
  3856.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3857.             Continue
  3858.         CompIPDARPrefix:
  3859.             AffixType := "Comp. Prefix"
  3860.             ValueRange := LookupAffixData(IPDARPath, ItemLevel, CurrValue, "", CurrTier)
  3861.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3862.             ARPartial := ARBracket
  3863.             Continue
  3864.         CompIPDARPrefixPrefix:
  3865.             NumPrefixes += 1
  3866.             AffixType := "Comp. Prefix+Prefix"
  3867.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  3868.             ARPartial := ARBracket
  3869.             Continue
  3870.         }
  3871.        
  3872.         IfInString, A_LoopField, increased Stun Recovery
  3873.         {
  3874.             AffixType := "Prefix"
  3875.             If (HasHybridDefences)
  3876.             {
  3877.                 AffixType := "Comp. Prefix"
  3878.                 BSRecAffixPath := "data\StunRecovery_Hybrid.txt"
  3879.                 BSRecAffixBracket := LookupAffixBracket(BSRecAffixPath, ItemLevel, CurrValue)
  3880.                 If (Not IsValidBracket(BSRecAffixBracket))
  3881.                 {
  3882.                     CompStatAffixType =
  3883.                     If (HasIncrArmourAndEvasion)
  3884.                     {
  3885.                         PartialAffixString := "increased Armour and Evasion"
  3886.                     }
  3887.                     If (HasIncrEvasionAndES)
  3888.                     {
  3889.                         PartialAffixString := "increased Evasion and Energy Shield"
  3890.                     }
  3891.                     If (HasIncrArmourAndES)
  3892.                     {
  3893.                         PartialAffixString := "increased Armour and Energy Shield"
  3894.                     }
  3895.                     CompStatAffixType := GetAffixTypeFromProcessedLine(PartialAffixString)
  3896.                     If (BSRecPartial)
  3897.                     {
  3898.                         If (WithinBounds(BSRecPartial, CurrValue))
  3899.                         {
  3900.                             IfInString, CompStatAffixType, Comp. Prefix
  3901.                             {
  3902.                                 AffixType := CompStatAffixType
  3903.                             }
  3904.                         }
  3905.                         Else
  3906.                         {
  3907.                             If (NumSuffixes < 3)
  3908.                             {
  3909.                                 AffixType := "Comp. Prefix+Suffix"
  3910.                                 BSRecAffixBracket := LookupRemainingAffixBracket("data\StunRecovery_Suffix.txt", ItemLevel, CurrValue, BSRecPartial)
  3911.                                 If (Not IsValidBracket(BSRecAffixBracket))
  3912.                                 {
  3913.                                     AffixType := "Comp. Prefix+Prefix"
  3914.                                     BSRecAffixBracket := LookupAffixBracket("data\StunRecovery_Prefix.txt", ItemLevel, CurrValue)
  3915.                                     If (Not IsValidBracket(BSRecAffixBracket))
  3916.                                     {
  3917.                                         If (CompStatAffixType == "Comp. Prefix+Prefix" and NumSuffixes < 3)
  3918.                                         {
  3919.                                             AffixType := "Comp. Prefix+Suffix"
  3920.                                             BSRecSuffixBracket := LookupAffixBracket("data\StunRecovery_Suffix.txt", ItemLevel, BSRest)
  3921.                                             NumSuffixes += 1
  3922.                                             If (Not IsValidBracket(BSRecSuffixBracket))
  3923.                                             {
  3924.                                                 ; TODO: properly deal with this quick fix!
  3925.                                                 ;
  3926.                                                 ; if this point is reached this means that the parts that give to
  3927.                                                 ; increased armor/evasion/es/hybrid + stun recovery need to fully be
  3928.                                                 ; re-evaluated.
  3929.                                                 ;
  3930.                                                 ; take an ilvl 62 item with these 2 lines:
  3931.                                                 ;
  3932.                                                 ;   118% increased Armour and Evasion
  3933.                                                 ;   24% increased Stun Recovery
  3934.                                                 ;
  3935.                                                 ; Since it's ilvl 62, we assume the hybrid + stun recovery bracket to be the
  3936.                                                 ; highest possible (lvl 60 bracket), which is 42-50. So that's max 50 of the
  3937.                                                 ; 118 dealth with.
  3938.                                                 ; Consequently, that puts the stun recovery partial at 14-15 for the lvl 60 bracket.
  3939.                                                 ; This now leaves, 68 of hybrid defence to account for, which we can do by assuming
  3940.                                                 ; the remainder to come from a hybrid defence prefix. So that's incr. Armour and Evasion
  3941.                                                 ; identified as CP+P
  3942.                                                 ; However, here come's the problem, our lvl 60 bracket had 14-15 stun recovery which
  3943.                                                 ; assuming max, leaves 9 remainder (24-15) to account for. Should be easy, right?
  3944.                                                 ; Just assume the rest comes from a stun recovery suffix and look it up. Except the
  3945.                                                 ; lowest possible entry for a stun recovery suffix is 11! Leaving us with the issues that
  3946.                                                 ; we know that CP+P is right for the hybrid + stun recovery line and CP+S is right for the
  3947.                                                 ; stun recovery line.
  3948.                                                 ; Most likely, what is wrong is the assumption earlier to take the highest possible
  3949.                                                 ; hybrid + stun recovery bracket. Problem is that wasn't apparent when hybrid defences
  3950.                                                 ; was processed.
  3951.                                                 ; At this point, a quick fix what I am doing is I just look up the complete stun recovery
  3952.                                                 ; value as if it were a suffix completely but still mark it as CP+S.
  3953.                                                 ; To deal with this correctly I would need to reprocess the hybrid + stun recovery line here
  3954.                                                 ; with a different ratio of the CP part to the P part to get a lower BSRecPartial.
  3955.                                                 ;
  3956.                                                 BSRecSuffixBracket := LookupAffixBracket("data\StunRecovery_Suffix.txt", ItemLevel, CurrValue)
  3957.                                                 ValueRange := LookupAffixBracket("data\StunRecovery_Suffix.txt", ItemLevel, CurrValue)
  3958.                                                 ValueRange := MarkAsGuesstimate(ValueRange)
  3959.                                             }
  3960.                                             Else
  3961.                                             {
  3962.                                                 ValueRange := AddRange(BSRecSuffixBracket, BSRecPartial)
  3963.                                                 ValueRange := MarkAsGuesstimate(ValueRange)
  3964.                                             }
  3965.                                         }
  3966.                                         Else
  3967.                                         {
  3968.                                             AffixType := "Suffix"
  3969.                                             ValueRange := LookupAffixData("data\StunRecovery_Suffix.txt", ItemLevel, CurrValue, "", CurrTier)
  3970.                                             If (NumSuffixes < 3)
  3971.                                             {
  3972.                                                 NumSuffixes += 1
  3973.                                             }
  3974.                                             ChangeAffixDetailLine(PartialAffixString, "Comp. Prefix" , "Prefix")
  3975.                                         }
  3976.                                     }
  3977.                                     Else
  3978.                                     {
  3979.                                         If (NumPrefixes < 3)
  3980.                                         {
  3981.                                             NumPrefixes += 1
  3982.                                         }
  3983.                                     }
  3984.                                 }
  3985.                                 Else
  3986.                                 {
  3987.                                     NumSuffixes += 1
  3988.                                     ValueRange := AddRange(BSRecPartial, BSRecAffixBracket)
  3989.                                     ValueRange := MarkAsGuesstimate(ValueRange)
  3990.                                 }
  3991.                             }
  3992.                         }
  3993.                     }
  3994.                     Else
  3995.                     {
  3996.                         ; Simple Stun Rec suffix
  3997.                         AffixType := "Suffix"
  3998.                         ValueRange := LookupAffixData("data\StunRecovery_Suffix.txt", ItemLevel, CurrValue, "", CurrTier)
  3999.                         NumSuffixes += 1
  4000.                     }
  4001.                 }
  4002.                 Else
  4003.                 {
  4004.                     ValueRange := LookupAffixData(BSRecAffixPath, ItemLevel, CurrValue, "", CurrTier)
  4005.                 }
  4006.             }
  4007.             Else
  4008.             {
  4009.                 AffixType := "Comp. Prefix"
  4010.                 If (HasIncrArmour)
  4011.                 {
  4012.                     PartialAffixString := "increased Armour"
  4013.                     BSRecAffixPath := "data\StunRecovery_Armour.txt"
  4014.                 }
  4015.                 If (HasIncrEvasion)
  4016.                 {
  4017.                     PartialAffixString := "increased Evasion Rating"
  4018.                     BSRecAffixPath := "data\StunRecovery_Evasion.txt"
  4019.                 }
  4020.                 If (HasIncrEnergyShield)
  4021.                 {
  4022.                     PartialAffixString := "increased Energy Shield"
  4023.                     BSRecAffixPath := "data\StunRecovery_EnergyShield.txt"
  4024.                 }
  4025.                 BSRecAffixBracket := LookupAffixBracket(BSRecAffixPath, ItemLevel, CurrValue)
  4026.                 If (Not IsValidBracket(BSRecAffixBracket))
  4027.                 {
  4028.                     CompStatAffixType := GetAffixTypeFromProcessedLine(PartialAffixString)
  4029.                     If (BSRecPartial)
  4030.                     {
  4031.                         If (WithinBounds(BSRecPartial, CurrValue))
  4032.                         {
  4033.                             IfInString, CompStatAffixType, Comp. Prefix
  4034.                             {
  4035.                                 AffixType := CompStatAffixType
  4036.                             }
  4037.                         }
  4038.                         Else
  4039.                         {
  4040.                             If (NumSuffixes < 3)
  4041.                             {
  4042.                                 AffixType := "Comp. Prefix+Suffix"
  4043.                                 BSRecAffixBracket := LookupRemainingAffixBracket("data\StunRecovery_Suffix.txt", ItemLevel, CurrValue, BSRecPartial)
  4044.                                 If (Not IsValidBracket(BSRecAffixBracket))
  4045.                                 {
  4046.                                     AffixType := "Comp. Prefix+Prefix"
  4047.                                     BSRecAffixBracket := LookupAffixBracket("data\StunRecovery_Prefix.txt", ItemLevel, CurrValue)
  4048.                                     If (Not IsValidBracket(BSRecAffixBracket))
  4049.                                     {
  4050.                                         AffixType := "Suffix"
  4051.                                         ValueRange := LookupAffixData("data\StunRecovery_Suffix.txt", ItemLevel, CurrValue, "", CurrTier)
  4052.                                         If (NumSuffixes < 3)
  4053.                                         {
  4054.                                             NumSuffixes += 1
  4055.                                         }
  4056.                                         ChangeAffixDetailLine(PartialAffixString, "Comp. Prefix" , "Prefix")
  4057.                                     }
  4058.                                     Else
  4059.                                     {
  4060.                                         If (NumPrefixes < 3)
  4061.                                         {
  4062.                                             NumPrefixes += 1
  4063.                                         }
  4064.                                     }
  4065.  
  4066.                                 }
  4067.                                 Else
  4068.                                 {
  4069.                                     NumSuffixes += 1
  4070.                                     ValueRange := AddRange(BSRecPartial, BSRecAffixBracket)
  4071.                                     ValueRange := MarkAsGuesstimate(ValueRange)
  4072.                                 }
  4073.                             }
  4074.                         }
  4075.                     }
  4076.                     Else
  4077.                     {
  4078.                         BSRecSuffixPath := "data\StunRecovery_Suffix.txt"
  4079.                         BSRecSuffixBracket := LookupAffixBracket(BSRecSuffixPath, ItemLevel, CurrValue)
  4080.                         If (IsValidBracket(BSRecSuffixBracket))
  4081.                         {
  4082.                             AffixType := "Suffix"
  4083.                             ValueRange := LookupAffixData(BSRecSuffixPath, ItemLevel, CurrValue, "", CurrTier)
  4084.                             If (NumSuffixes < 3)
  4085.                             {
  4086.                                 NumSuffixes += 1
  4087.                             }
  4088.                         }
  4089.                         Else
  4090.                         {
  4091.                             BSRecPrefixPath := "data\StunRecovery_Prefix.txt"
  4092.                             BSRecPrefixBracket := LookupAffixBracket(BSRecPrefixPath, ItemLevel, CurrValue)
  4093.                             ValueRange := LookupAffixData(BSRecPrefixPath, ItemLevel, CurrValue, "", CurrTier)
  4094.                         }
  4095.                     }
  4096.                 }
  4097.                 Else
  4098.                 {
  4099.                     ValueRange := LookupAffixData(BSRecAffixPath, ItemLevel, CurrValue, "", CurrTier)
  4100.                 }
  4101.             }
  4102.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  4103.             Continue
  4104.         }
  4105.        
  4106.         ; AR is one tough beast... currently there are the following affixes affecting AR:
  4107.         ;   1) "Accuracy Rating" (Suffix)
  4108.         ;   2) "Local Accuracy Rating" (Suffix)
  4109.         ;   3) "Light Radius / + Accuracy Rating" (Suffix) - only the first 2 entries, bc last entry combines LR with #% increased Accuracy Rating instead!
  4110.         ;   4) "Local Physical Dmg +% / Local Accuracy Rating" (Prefix)
  4111.  
  4112.         ; The difficulty lies in those cases that combine multiples of these affixes into one final display value.
  4113.         ; Currently I try and tackle this by using a trickle-through partial balance approach. That is, go from
  4114.         ; most special case to most normal, while subtracting the value that each case most likely contributes
  4115.         ; until you have a value left that can be found in the most nominal case.
  4116.         ;
  4117.         ; Important to note here:
  4118.         ;   ARPartial will be set during the "increased Physical Damage" case above
  4119.        
  4120.         IfInString, A_LoopField, to Accuracy Rating
  4121.         {
  4122.             ; Trickle-through order:
  4123.             ;   1) increased AR, Light Radius, all except Belts, Comp. Suffix
  4124.             ;   2) to AR, Light Radius, all except Belts, Comp. Suffix
  4125.             ;   3) increased Phys Damage, to AR, Weapons, Prefix
  4126.             ;   4) to AR, all except Belts, Suffix
  4127.  
  4128.             ValueRangeAR := "0-0"
  4129.             AffixType := ""
  4130.             IPDAffixType := GetAffixTypeFromProcessedLine("increased Physical Damage")
  4131.             If (HasIncrLightRadius and Not HasIncrAccuracyRating)
  4132.             {
  4133.                 ; "of Shining" and "of Light"
  4134.                 LightRadiusValue := ExtractValueFromAffixLine(ItemDataChunk, "increased Light Radius")
  4135.                
  4136.                 ; Get bracket level of the light radius so we can look up the corresponding AR bracket
  4137.                 BracketLevel := 0
  4138.                 LookupAffixBracket("data\LightRadius_AccuracyRating.txt", ItemLevel, LightRadiusValue, BracketLevel)
  4139.                 ARLRBracket := LookupAffixBracket("data\AccuracyRating_LightRadius.txt", BracketLevel)
  4140.  
  4141.                 AffixType := AffixType . "Comp. Suffix"
  4142.                 ValueRange := LookupAffixData("data\AccuracyRating_LightRadius.txt", ItemLevel, CurrValue, "", CurrTier)
  4143.                 NumSuffixes += 1
  4144.  
  4145.                 If (ARPartial)
  4146.                 {
  4147.                     ; Append this affix' contribution to our partial AR range
  4148.                     ARPartial := AddRange(ARPartial, ARLRBracket)
  4149.                 }
  4150.                 ; Test if candidate range already covers current  AR value
  4151.                 If (WithinBounds(ARLRBracket, CurrValue))
  4152.                 {
  4153.                     Goto, FinalizeAR
  4154.                 }
  4155.                 Else
  4156.                 {
  4157.                     AffixType := "Comp. Suffix+Suffix"
  4158.                     If (HasIncrPhysDmg)
  4159.                     {
  4160.                         If (ARPartial)
  4161.                         {
  4162.                             CombinedRange := AddRange(ARLRBracket, ARPartial)
  4163.                             AffixType := "Comp. Prefix+Comp. Suffix"
  4164.                            
  4165.                             If (WithinBounds(CombinedRange, CurrValue))
  4166.                             {
  4167.                                 If (NumPrefixes < 3)
  4168.                                 {
  4169.                                     NumPrefixes += 1
  4170.                                 }
  4171.                                 ValueRange := CombinedRange
  4172.                                 ValueRange := MarkAsGuesstimate(ValueRange)
  4173.                                 Goto, FinalizeAR
  4174.                             }
  4175.                             Else
  4176.                             {
  4177.                                 NumSuffixes -= 1
  4178.                             }
  4179.                         }
  4180.  
  4181.                         If (InStr(IPDAffixType, "Comp. Prefix"))
  4182.                         {
  4183. ;                            AffixType := "Comp. Prefix+Comp. Suffix+Suffix"
  4184.                             If (NumPrefixes < 3)
  4185.                             {
  4186.                                 NumPrefixes += 1
  4187.                             }
  4188.                         }
  4189.                     }
  4190.                     ARBracket := LookupRemainingAffixBracket("data\AccuracyRating_Global.txt", ItemLevel, CurrValue, ARLRBracket)
  4191.                     ValueRange := AddRange(ARBracket, ARLRBracket)
  4192.                     ValueRange := MarkAsGuesstimate(ValueRange)
  4193.                     NumSuffixes += 1
  4194.                     Goto, FinalizeAR
  4195.                 }
  4196.             }
  4197.             If (ItemBaseType == "Weapon" and HasIncrPhysDmg)
  4198.             {
  4199.                 ; This is one of the trickiest cases currently (EDIT: nope, I have seen trickier stuff still ;D)
  4200.                 ;
  4201.                 ; If this If-construct is reached that means the item has multiple composites:
  4202.                 ;   "To Accuracy Rating / Increased Light Radius" and
  4203.                 ;   "Increased Physical Damage / To Accuracy Rating".
  4204.                 ;
  4205.                 ; On top of that it might also contain part "To Accuracy Rating" suffix, all of which are
  4206.                 ; concatenated into one single "to Accuracy Rating" entry.
  4207.                 ; Currently it handles most cases, if not all, but I still have a feeling I am missing
  4208.                 ; something... (EDIT: a feeling I won't be able to shake ever with master crafted affixes now)
  4209.                 ;
  4210.                 ; GGG, if you are reading this: please add special markup for affix compositions!
  4211.                 ;
  4212.                 If (ARPartial)
  4213.                 {
  4214.                     If (WithinBounds(ARPartial, CurrValue))
  4215.                     {
  4216.                         AffixType := "Comp. Prefix"
  4217.                         If (NumPrefixes < 3)
  4218.                         {
  4219.                             NumPrefixes += 1
  4220.                         }
  4221.                         ValueRange := LookupAffixData("data\AccuracyRating_IncrPhysDamage.txt", ItemLevel, RangeMid(ARPartial), "", CurrTier)
  4222.                         Goto, FinalizeAR
  4223.                     }
  4224.  
  4225.                     ARPartialMid := RangeMid(ARPartial)
  4226.                     ARRest := CurrValue - ARPartialMid
  4227.                     If (ItemSubType == "Mace" and ItemGripType == "2H")
  4228.                     {
  4229.                         ARBracket := LookupAffixBracket("data\AccuracyRating_Global.txt", ItemLevel, ARRest)
  4230.                     }
  4231.                     Else
  4232.                     {
  4233.                         ARBracket := LookupAffixBracket("data\AccuracyRating_Local.txt", ItemLevel, ARRest)
  4234.                     }
  4235.                    
  4236.                     If (IsValidBracket(ARBracket))
  4237.                     {
  4238.                         AffixType := "Comp. Prefix+Suffix"
  4239.                         If (NumSuffixes < 3)
  4240.                         {
  4241.                             NumSuffixes += 1
  4242.                         }
  4243.                         Else
  4244.                         {
  4245.                             AffixType := "Comp. Prefix"
  4246.                             If (NumPrefixes < 3)
  4247.                             {
  4248.                                 NumPrefixes += 2
  4249.                             }
  4250.                         }
  4251.                         NumPrefixes += 1
  4252.                         ValueRange := AddRange(ARBracket, ARPartial)
  4253.                         ValueRange := MarkAsGuesstimate(ValueRange)
  4254.  
  4255.                         Goto, FinalizeAR
  4256.                     }
  4257.                 }
  4258.                 Else
  4259.                 {
  4260.                     ActualValue := CurrValue
  4261.                 }
  4262.  
  4263.                 ValueRangeAR := LookupAffixBracket("data\AccuracyRating_Global.txt", ItemLevel, ActualValue)
  4264.                 If (IsValidBracket(ValueRangeAR))
  4265.                 {
  4266.                     If (NumPrefixes >= 3)
  4267.                     {
  4268.                         AffixType := "Suffix"
  4269.                         If (NumSuffixes < 3)
  4270.                         {
  4271.                             NumSuffixes += 1
  4272.                         }
  4273.                         ValueRange := LookupAffixData("data\AccuracyRating_Local.txt", ItemLevel, ActualValue, "", CurrTier)
  4274.                     }
  4275.                     Else
  4276.                     {
  4277.                         IfInString, IPDAffixType, Comp. Prefix
  4278.                         {
  4279.                             AffixType := "Comp. Prefix"
  4280.                         }
  4281.                         Else
  4282.                         {
  4283.                             AffixType := "Prefix"
  4284.                         }
  4285.                         NumPrefixes += 1
  4286.                     }
  4287.                     Goto, FinalizeAR
  4288.                 }
  4289.                 Else
  4290.                 {
  4291.                     ARValueRest := CurrValue - (RangeMid(ValueRangeAR))
  4292.                     If (HasIncrLightRadius and Not HasIncrAccuracyRating)
  4293.                     {
  4294.                         AffixType := "Comp. Prefix+Comp. Suffix+Suffix"
  4295.                     }
  4296.                     Else
  4297.                     {
  4298.                         AffixType := "Comp. Prefix+Suffix"
  4299.                     }
  4300.                     NumPrefixes += 1
  4301.                     NumSuffixes += 1
  4302.                     ;~ ValueRange := LookupAffixData("data\AccuracyRating_IncrPhysDamage.txt", ItemLevel, CurrValue, "", CurrTier)
  4303.                     ValueRange := AddRange(ARPartial, ValueRangeAR)
  4304.                     ValueRange := MarkAsGuesstimate(ValueRange)
  4305.                 }
  4306.                 ; NumPrefixes should be incremented already by "increased Physical Damage" case
  4307.                 Goto, FinalizeAR
  4308.             }
  4309.             AffixType := "Suffix"
  4310.             ValueRange := LookupAffixData("data\AccuracyRating_Global.txt", ItemLevel, CurrValue, "", CurrTier)
  4311.             NumSuffixes += 1
  4312.             Goto, FinalizeAR
  4313.  
  4314.         FinalizeAR:
  4315.             If (StrLen(ARAffixTypePartial) > 0 and (Not InStr(AffixType, ARAffixTypePartial)))
  4316.             {
  4317.                 AffixType := ARAffixTypePartial . "+" . AffixType
  4318.                 If (InStr(ARAffixTypePartial, "Prefix") and NumPrefixes < 3)
  4319.                 {
  4320.                     NumPrefixes += 1
  4321.                 }
  4322.                 Else If (InStr(ARAffixTypePartial, "Suffix") and NumSuffixes < 3)
  4323.                 {
  4324.                     NumSuffixes += 1
  4325.                 }
  4326.                 ARAffixTypePartial =
  4327.             }
  4328.             AppendAffixInfo(MakeAffixDetailLine(A_LoopField, AffixType, ValueRange, CurrTier), A_Index)
  4329.             Continue
  4330.         }
  4331.  
  4332.         IfInString, A_LoopField, increased Rarity
  4333.         {
  4334.             ActualValue := CurrValue
  4335.             If (NumSuffixes <= 3)
  4336.             {
  4337.                 ValueRange := LookupAffixBracket("data\IIR_Suffix.txt", ItemLevel, ActualValue)
  4338.                 ValueRangeAlt := LookupAffixBracket("data\IIR_Prefix.txt", ItemLevel, ActualValue)
  4339.             }
  4340.             Else
  4341.             {
  4342.                 ValueRange := LookupAffixBracket("data\IIR_Prefix.txt", ItemLevel, ActualValue)
  4343.                 ValueRangeAlt := LookupAffixBracket("data\IIR_Suffix.txt", ItemLevel, ActualValue)
  4344.             }
  4345.             If (Not IsValidBracket(ValueRange))
  4346.             {
  4347.                 If (Not IsValidBracket(ValueRangeAlt))
  4348.                 {
  4349.                     NumPrefixes += 1
  4350.                     NumSuffixes += 1
  4351.                     ; Try to reverse engineer composition of both ranges
  4352.                     PrefixDivisor := 1
  4353.                     SuffixDivisor := 1
  4354.                     Loop
  4355.                     {
  4356.                         ValueRangeSuffix := LookupAffixBracket("data\IIR_Suffix.txt", ItemLevel, Floor(ActualValue/SuffixDivisor))
  4357.                         ValueRangePrefix := LookupAffixBracket("data\IIR_Prefix.txt", ItemLevel, Floor(ActualValue/PrefixDivisor))
  4358.                         If (Not IsValidBracket(ValueRangeSuffix))
  4359.                         {
  4360.                             SuffixDivisor += 0.25
  4361.                         }
  4362.                         If (Not IsValidBracket(ValueRangePrefix))
  4363.                         {
  4364.                             PrefixDivisor += 0.25
  4365.                         }
  4366.                         If ((IsValidBracket(ValueRangeSuffix)) and (IsValidBracket(ValueRangePrefix)))
  4367.                         {
  4368.                             Break
  4369.                         }
  4370.                     }
  4371.                     ValueRange := AddRange(ValueRangePrefix, ValueRangeSuffix)
  4372.                     Goto, FinalizeIIRAsPrefixAndSuffix
  4373.                 }
  4374.                 Else
  4375.                 {
  4376.                     ValueRange := ValueRangePrefix
  4377.                     Goto, FinalizeIIRAsPrefix
  4378.                 }
  4379.             }
  4380.             Else
  4381.             {
  4382.                 If (NumSuffixes >= 3) {
  4383.                     Goto, FinalizeIIRAsPrefix
  4384.                 }
  4385.                 Goto, FinalizeIIRAsSuffix
  4386.             }
  4387.  
  4388.             FinalizeIIRAsPrefix:
  4389.                 NumPrefixes += 1
  4390.                 ValueRange := LookupAffixData("data\IIR_Prefix.txt", ItemLevel, ActualValue, "", CurrTier)
  4391.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix", ValueRange, CurrTier), A_Index)
  4392.                 Continue
  4393.  
  4394.             FinalizeIIRAsSuffix:
  4395.                 NumSuffixes += 1
  4396.                 ValueRange := LookupAffixData("data\IIR_Suffix.txt", ItemLevel, ActualValue, "", CurrTier)
  4397.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Suffix", ValueRange, CurrTier), A_Index)
  4398.                 Continue
  4399.  
  4400.             FinalizeIIRAsPrefixAndSuffix:
  4401.                 ValueRange := MarkAsGuesstimate(ValueRange)
  4402.                 AppendAffixInfo(MakeAffixDetailLine(A_LoopField, "Prefix+Suffix", ValueRange, CurrTier), A_Index)
  4403.                 Continue
  4404.         }
  4405.     }
  4406.    
  4407.     ; --- CRAFTED --- (Preliminary Support)
  4408.    
  4409.     Loop, Parse, ItemDataChunk, `n, `r
  4410.     {    
  4411.         If StrLen(A_LoopField) = 0
  4412.         {
  4413.             Break ; Not interested in blank lines
  4414.         }
  4415.         IfInString, ItemDataChunk, Unidentified
  4416.         {
  4417.             Break ; Not interested in unidentified items
  4418.         }
  4419.                
  4420.         IfInString, A_LoopField, Can have multiple Crafted Mods
  4421.         {
  4422.             AppendAffixInfo(A_Loopfield, A_Index)
  4423.         }
  4424.         IfInString, A_LoopField, to Weapon range
  4425.         {
  4426.             AppendAffixInfo(A_Loopfield, A_Index)
  4427.         }
  4428.     }
  4429.    
  4430.     AffixTotals.NumPrefixes := NumPrefixes
  4431.     AffixTotals.NumSuffixes := NumSuffixes
  4432. }
  4433.  
  4434. ; Change a detail line that was already processed and added to the
  4435. ; AffixLines "stack". This can be used for example to change the
  4436. ; affix type when more is known about a possible affix combo.
  4437. ;
  4438. ; For example with a IPD / AR combo, if IPD was thought to be a
  4439. ; Prefix but later (when processing AR) found to be a Composite
  4440. ; Prefix.
  4441. ChangeAffixDetailLine(PartialAffixString, SearchRegex, ReplaceRegex)
  4442. {
  4443.     Global AffixLines
  4444.     NumAffixLines := AffixLines.MaxIndex()
  4445.     Loop, %NumAffixLines%
  4446.     {
  4447.         CurAffixLine := AffixLines[A_Index]
  4448.         IfInString, CurAffixLine, %PartialAffixString%
  4449.         {
  4450.             NewLine := RegExReplace(CurAffixLine, SearchRegex, ReplaceRegex)
  4451.             AffixLines.Set(A_Index, NewLine)
  4452.             return True
  4453.         }
  4454.     }
  4455.     return False
  4456. }
  4457.  
  4458. ExtractValueFromAffixLine(ItemDataChunk, PartialAffixString)
  4459. {
  4460.     Loop, Parse, ItemDataChunk, `n, `r
  4461.     {
  4462.         If StrLen(A_LoopField) = 0
  4463.         {
  4464.             Break ; Not interested in blank lines
  4465.         }
  4466.         IfInString, ItemDataChunk, Unidentified
  4467.         {
  4468.             Break ; Not interested in unidentified items
  4469.         }
  4470.  
  4471.         CurrValue := GetActualValue(A_LoopField)
  4472.  
  4473.         IfInString, A_LoopField, %PartialAffixString%
  4474.         {
  4475.             return CurrValue
  4476.         }
  4477.     }
  4478. }
  4479.  
  4480. ResetAffixDetailVars()
  4481. {
  4482.     Global AffixLines, AffixTotals, Globals
  4483.     AffixLines.Reset()
  4484.     AffixTotals.Reset()
  4485.     Globals.Set("MarkedAsGuess", False)
  4486. }
  4487.  
  4488. IsEmptyString(String)
  4489. {
  4490.     If (StrLen(String) == 0)
  4491.     {
  4492.         return True
  4493.     }
  4494.     Else
  4495.     {
  4496.         String := RegExReplace(String, "[\r\n ]", "")
  4497.         If (StrLen(String) < 1)
  4498.         {
  4499.             return True
  4500.         }
  4501.     }
  4502.     return False
  4503. }
  4504.  
  4505. PreProcessContents(CBContents)
  4506. {
  4507.     ; --- Place fixes for data inconsistencies here ---
  4508.  
  4509.     ; Remove the line that indicates an item cannot be used due to missing character stats
  4510.     Needle := "You cannot use this item. Its stats will be ignored. Please remove it.`r`n--------`r`n"
  4511.     StringReplace, CBContents, CBContents, %Needle%,
  4512.     ; Replace double seperator lines with one seperator line
  4513.     Needle := "--------`r`n--------`r`n"
  4514.     StringReplace, CBContents, CBContents, %Needle%, --------`r`n, All
  4515.    
  4516.     return CBContents
  4517. }
  4518.  
  4519. PostProcessData(ParsedData)
  4520. {
  4521.     Global Opts
  4522.    
  4523.     Result := ParsedData
  4524.     If (Opts.CompactAffixTypes > 0)
  4525.     {
  4526.         StringReplace, TempResult, ParsedData, --------`n, ``, All  
  4527.         StringSplit, ParsedDataChunks, TempResult, ``
  4528.        
  4529.         Result =
  4530.         Loop, %ParsedDataChunks0%
  4531.         {
  4532.             CurrChunk := ParsedDataChunks%A_Index%
  4533.             If IsEmptyString(CurrChunk)
  4534.             {
  4535.                 Continue
  4536.             }
  4537.             If (InStr(CurrChunk, "Comp.") and Not InStr(CurrChunk, "Affixes"))
  4538.             {
  4539.                 CurrChunk := RegExReplace(CurrChunk, "Comp\. ", "C")
  4540.             }
  4541.             If (InStr(CurrChunk, "Suffix") and Not InStr(CurrChunk, "Affixes"))
  4542.             {
  4543.                 CurrChunk := RegExReplace(CurrChunk, "Suffix", "S")
  4544.             }
  4545.             If (InStr(CurrChunk, "Prefix") and Not InStr(CurrChunk, "Affixes"))
  4546.             {
  4547.                 CurrChunk := RegExReplace(CurrChunk, "Prefix", "P")
  4548.             }
  4549.             If (A_Index < ParsedDataChunks0)
  4550.             {
  4551.                 Result := Result . CurrChunk . "--------`r`n"
  4552.             }
  4553.             Else
  4554.             {
  4555.                 Result := Result . CurrChunk
  4556.             }
  4557.         }
  4558.     }
  4559.     return Result
  4560. }
  4561.  
  4562. ParseClipBoardChanges()
  4563. {
  4564.     Global Opts, Globals
  4565.  
  4566.     CBContents := GetClipboardContents()
  4567.     CBContents := PreProcessContents(CBContents)
  4568.  
  4569.     Globals.Set("ItemText", CBContents)
  4570.    
  4571.     If (GetKeyState("Shift"))
  4572.     {
  4573.         Globals.Set("TierRelativeToItemLevelOverride", !Opts.TierRelativeToItemLevel)
  4574.     }
  4575.     Else
  4576.     {
  4577.         Globals.Set("TierRelativeToItemLevelOverride", Opts.TierRelativeToItemLevel)
  4578.     }
  4579.  
  4580.     ParsedData := ParseItemData(CBContents)
  4581.     ParsedData := PostProcessData(ParsedData)
  4582.  
  4583.     If (Opts.PutResultsOnClipboard > 0)
  4584.     {
  4585.         SetClipboardContents(ParsedData)
  4586.     }
  4587.     ShowToolTip(ParsedData)
  4588. }
  4589.  
  4590. AssembleDamageDetails(FullItemData)
  4591. {
  4592.     PhysLo := 0
  4593.     PhysHi := 0
  4594.     Quality := 0
  4595.     AttackSpeed := 0
  4596.     PhysMult := 0
  4597.     ChaoLo := 0
  4598.     ChaoHi := 0
  4599.     ColdLo := 0
  4600.     ColdHi := 0
  4601.     FireLo := 0
  4602.     FireHi := 0
  4603.     LighLo := 0
  4604.     LighHi := 0
  4605.  
  4606.     Loop, Parse, FullItemData, `n, `r
  4607.     {        
  4608.         ; Get quality
  4609.         IfInString, A_LoopField, Quality:
  4610.         {
  4611.             StringSplit, Arr, A_LoopField, %A_Space%, +`%
  4612.             Quality := Arr2
  4613.             Continue
  4614.         }
  4615.        
  4616.         ; Get total physical damage
  4617.         IfInString, A_LoopField, Physical Damage:
  4618.         {
  4619.             StringSplit, Arr, A_LoopField, %A_Space%
  4620.             StringSplit, Arr, Arr3, -
  4621.             PhysLo := Arr1
  4622.             PhysHi := Arr2
  4623.             Continue
  4624.         }
  4625.        
  4626.         ; Fix for Elemental damage only weapons. Like the Oro's Sacrifice
  4627.         IfInString, A_LoopField, Elemental Damage:
  4628.         {
  4629.             Continue
  4630.         }
  4631.  
  4632.         ; Get attack speed
  4633.         IfInString, A_LoopField, Attacks per Second:
  4634.         {
  4635.             StringSplit, Arr, A_LoopField, %A_Space%
  4636.             AttackSpeed := Arr4
  4637.             Continue
  4638.         }
  4639.        
  4640.         ; Get percentage physical damage increase
  4641.         IfInString, A_LoopField, increased Physical Damage
  4642.         {
  4643.             StringSplit, Arr, A_LoopField, %A_Space%, `%
  4644.             PhysMult := Arr1
  4645.             Continue
  4646.         }
  4647.        
  4648.         ; Lines to skip fix for converted type damage. Like the Voltaxic Rift
  4649.         IfInString, A_LoopField, Converted to
  4650.             Goto, SkipDamageParse
  4651.         IfInString, A_LoopField, can Shock
  4652.             Goto, SkipDamageParse
  4653.  
  4654.         ; Lines to skip for weapons that alter damage based on if equipped as
  4655.         ; main or off hand. In that case skipp the off hand calc and just use
  4656.         ; main hand as determining factor. Examples: Dyadus, Wings of Entropy
  4657.         IfInString, A_LoopField, in Off Hand
  4658.             Goto, SkipDamageParse
  4659.  
  4660.         ; Parse elemental damage
  4661.         ParseElementalDamage(A_LoopField, "Chaos", ChaoLo, ChaoHi)
  4662.         ParseElementalDamage(A_LoopField, "Cold", ColdLo, ColdHi)
  4663.         ParseElementalDamage(A_LoopField, "Fire", FireLo, FireHi)
  4664.         ParseElementalDamage(A_LoopField, "Lightning", LighLo, LighHi)
  4665.        
  4666.         SkipDamageParse:
  4667.             DoNothing := True
  4668.     }
  4669.    
  4670.     Result =
  4671.  
  4672.     SetFormat, FloatFast, 5.1
  4673.     PhysDps := ((PhysLo + PhysHi) / 2) * AttackSpeed
  4674.     EleDps := ((ChaoLo + ChaoHi + ColdLo + ColdHi + FireLo + FireHi + LighLo + LighHi) / 2) * AttackSpeed
  4675.     TotalDps := PhysDps + EleDps
  4676.    
  4677.     Result = %Result%`nPhys DPS:   %PhysDps%`nElem DPS:   %EleDps%`nTotal DPS:  %TotalDps%
  4678.    
  4679.     ; Only show Q20 values if item is not Q20
  4680.     If (Quality < 20) {
  4681.         TotalPhysMult := (PhysMult + Quality + 100) / 100
  4682.         BasePhysDps := PhysDps / TotalPhysMult
  4683.         Q20Dps := BasePhysDps * ((PhysMult + 120) / 100) + EleDps
  4684.        
  4685.         Result = %Result%`nQ20 DPS:    %Q20Dps%
  4686.     }
  4687.  
  4688.     return Result
  4689. }
  4690.  
  4691. ParseItemName(ItemDataChunk, ByRef ItemName, ByRef ItemTypeName)
  4692. {
  4693.     Loop, Parse, ItemDataChunk, `n, `r
  4694.     {
  4695.         If (A_Index == 1)
  4696.         {
  4697.             IfNotInString, A_LoopField, Rarity:
  4698.             {
  4699.                 return
  4700.             }
  4701.             Else
  4702.             {
  4703.                 Continue
  4704.             }
  4705.         }
  4706.         If (StrLen(A_LoopField) == 0 or A_LoopField == "--------" or A_Index > 3)
  4707.         {
  4708.             return
  4709.         }
  4710.         If (A_Index = 2)
  4711.         {
  4712.             ItemName := A_LoopField
  4713.         }
  4714.         If (A_Index = 3)
  4715.         {
  4716.             ItemTypeName := A_LoopField
  4717.         }
  4718.     }
  4719. }
  4720.  
  4721. GemIsValuable(ItemName)
  4722. {
  4723.     Loop, Read, %A_ScriptDir%\data\ValuableGems.txt
  4724.     {
  4725.         Line := StripLineCommentRight(A_LoopReadLine)
  4726.         If (SkipLine(Line))
  4727.         {
  4728.             Continue
  4729.         }
  4730.         IfInString, ItemName, %Line%
  4731.         {
  4732.             return True
  4733.         }
  4734.     }
  4735.     return False
  4736. }
  4737.  
  4738. UniqueIsValuable(ItemName)
  4739. {
  4740.     Loop, Read, %A_ScriptDir%\data\ValuableUniques.txt
  4741.     {
  4742.         Line := StripLineCommentRight(A_LoopReadLine)
  4743.         If (SkipLine(Line))
  4744.         {
  4745.             Continue
  4746.         }
  4747.         IfInString, ItemName, %Line%
  4748.         {
  4749.             return True
  4750.         }
  4751.     }
  4752.     return False
  4753. }
  4754.  
  4755. GemIsDropOnly(ItemName)
  4756. {
  4757.     Loop, Read, %A_ScriptDir%\data\DropOnlyGems.txt
  4758.     {
  4759.         Line := StripLineCommentRight(A_LoopReadLine)
  4760.         If (SkipLine(Line))
  4761.         {
  4762.             Continue
  4763.         }
  4764.         IfInString, ItemName, %Line%
  4765.         {
  4766.             return True
  4767.         }
  4768.     }
  4769.     return False
  4770. }
  4771.  
  4772. ParseLinks(ItemDataText)
  4773. {
  4774.     HighestLink := 0
  4775.     Loop, Parse, ItemDataText, `n, `r
  4776.     {
  4777.         IfInString, A_LoopField, Sockets
  4778.         {
  4779.             LinksString := GetColonValue(A_LoopField)
  4780.             If (RegExMatch(LinksString, ".-.-.-.-.-."))
  4781.             {
  4782.                 HighestLink := 6
  4783.                 Break
  4784.             }
  4785.             If (RegExMatch(LinksString, ".-.-.-.-."))
  4786.             {
  4787.                 HighestLink := 5
  4788.                 Break
  4789.             }
  4790.             If (RegExMatch(LinksString, ".-.-.-."))
  4791.             {
  4792.                 HighestLink := 4
  4793.                 Break
  4794.             }
  4795.             If (RegExMatch(LinksString, ".-.-."))
  4796.             {
  4797.                 HighestLink := 3
  4798.                 Break
  4799.             }
  4800.             If (RegExMatch(LinksString, ".-."))
  4801.             {
  4802.                 HighestLink := 2
  4803.                 Break
  4804.             }
  4805.         }
  4806.     }
  4807.     return HighestLink
  4808. }
  4809.  
  4810. ; TODO: find a way to poll this date from the web!
  4811.  
  4812. ; Converts a currency stack to Chaos by looking up the
  4813. ; conversion ratio from CurrencyRates.txt
  4814. ConvertCurrency(ItemName, ItemStats)
  4815. {
  4816.     If (InStr(ItemName, "Shard"))
  4817.     {
  4818.         IsShard := True
  4819.         ItemName := "Orb of " . SubStr(ItemName, 1, -StrLen(" Shard"))
  4820.     }
  4821.     If (InStr(ItemName, "Fragment"))
  4822.     {
  4823.         IsFragment := True
  4824.         ItemName := "Scroll of Wisdom"
  4825.     }
  4826.     StackSize := SubStr(ItemStats, StrLen("Stack Size:  "))
  4827.     StringSplit, StackSizeParts, StackSize, /
  4828.     If (IsShard or IsFragment)
  4829.     {
  4830.         SetFormat, FloatFast, 5.3
  4831.         StackSize := StackSizeParts1 / StackSizeParts2
  4832.     }
  4833.     Else
  4834.     {
  4835.         SetFormat, FloatFast, 5.2
  4836.         StackSize := StackSizeParts1
  4837.     }
  4838.     ValueInChaos := 0
  4839.     Loop, Read, %A_ScriptDir%\data\CurrencyRates.txt
  4840.     {
  4841.         Line := StripLineCommentRight(A_LoopReadLine)
  4842.         If (SkipLine(Line))
  4843.         {
  4844.             Continue
  4845.         }
  4846.         IfInString, Line, %ItemName%
  4847.         {
  4848.             StringSplit, LineParts, Line, |
  4849.             ChaosRatio := LineParts2
  4850.             StringSplit, ChaosRatioParts,ChaosRatio, :
  4851.             ChaosMult := ChaosRatioParts2 / ChaosRatioParts1
  4852.             ValueInChaos := (ChaosMult * StackSize)
  4853.             return ValueInChaos
  4854.         }
  4855.     }
  4856.     return ValueInChaos
  4857. }
  4858.  
  4859. FindUnique(ItemName)
  4860. {
  4861.     Loop, Read, %A_ScriptDir%\data\Uniques.txt
  4862.     {
  4863.         Line := StripLineCommentRight(A_LoopReadLine)
  4864.         If (SkipLine(Line))
  4865.         {
  4866.             Continue
  4867.         }
  4868.         IfInString, Line, %ItemName%
  4869.         {
  4870.             return True
  4871.         }
  4872.     }
  4873.     return False
  4874. }
  4875.  
  4876. ; Strip comments at line end, e.g. "Bla bla bla ; comment" -> "Bla bla bla"
  4877. StripLineCommentRight(Line)
  4878. {
  4879.     IfNotInString, Line, `;
  4880.     {
  4881.         return Line
  4882.     }
  4883.     ProcessedLine := RegExReplace(Line, "(.+?)([ \t]*;.+)", "$1")
  4884.     If IsEmptyString(ProcessedLine)
  4885.     {
  4886.         return Line
  4887.     }
  4888.     return ProcessedLine
  4889. }
  4890.  
  4891. ; Return True if line begins with comment character (;)
  4892. ; or if it is blank (that is, it only has 2 characters
  4893. ; at most (newline and carriage return)
  4894. SkipLine(Line)
  4895. {
  4896.     IfInString, Line, `;
  4897.     {
  4898.         ; Comment
  4899.         return True
  4900.     }
  4901.     If (StrLen(Line) <= 2)
  4902.     {
  4903.         ; Blank line (at most \r\n)
  4904.         return True
  4905.     }
  4906.     return False
  4907. }
  4908.  
  4909. ; Parse unique affixes from text file database.
  4910. ; Has wanted side effect of populating AffixLines "array" vars.
  4911. ; return True if the unique was found the database
  4912. ParseUnique(ItemName)
  4913. {
  4914.     Global Opts, AffixLines
  4915.    
  4916.     Delim := "|"
  4917.     ResetAffixDetailVars()
  4918.     UniqueFound := False
  4919.     Loop, Read, %A_ScriptDir%\data\Uniques.txt
  4920.     {
  4921.         ALine := StripLineCommentRight(A_LoopReadLine)
  4922.         If (SkipLine(ALine))
  4923.         {
  4924.             Continue
  4925.         }
  4926.         IfInString, ALine, %ItemName%
  4927.         {
  4928.             StringSplit, LineParts, ALine, |
  4929.             NumLineParts := LineParts0
  4930.             NumAffixLines := NumLineParts-1 ; exclude item name at first pos
  4931.             UniqueFound := True
  4932.             AppendImplicitSep := False
  4933.             Idx := 1
  4934.             If (Opts.ShowAffixDetails == False)
  4935.             {
  4936.                 return UniqueFound
  4937.             }
  4938.             Loop, % (NumLineParts)
  4939.             {
  4940.                 If (A_Index > 1)
  4941.                 {
  4942.                     ProcessedLine =
  4943.                     CurLinePart := LineParts%A_Index%
  4944.                     IfInString, CurLinePart, :
  4945.                     {
  4946.                         StringSplit, CurLineParts, CurLinePart, :
  4947.                         AffixLine := CurLineParts2
  4948.                         ValueRange := CurLineParts1
  4949.                         IfInString, ValueRange, @
  4950.                         {
  4951.                             AppendImplicitSep := True
  4952.                             StringReplace, ValueRange, ValueRange, @
  4953.                         }
  4954.                         ; Make "Attacks per Second" float ranges to be like a double range.
  4955.                         ; Since a 2 decimal precision float value is 4 chars wide (#.##)
  4956.                         ; when including the radix point this means a float value range
  4957.                         ; is then 9 chars wide. Replacing the "-" with a "," effectively
  4958.                         ; makes it so that float ranges are treated as double ranges and
  4959.                         ; distributes the bounds over both value range fields. This may
  4960.                         ; or may not be desirable. On the plus side things will align
  4961.                         ; nicely, but on the negative side, it will be a bit unclearer that
  4962.                         ; both float values constitute a range and not two isolated values.
  4963.                         ;ValueRange := RegExReplace(ValueRange, "(\d+\.\d+)-(\d+\.\d+)", "$1,$2") ; DISABLED for now
  4964.                         IfInString, ValueRange, `,
  4965.                         {
  4966.                             ; Double range
  4967.                             StringSplit, VRParts, ValueRange, `,
  4968.                             LowerBound := VRParts1
  4969.                             UpperBound := VRParts2
  4970.                             StringSplit, LowerBoundParts, LowerBound, -
  4971.                             StringSplit, UpperBoundParts, UpperBound, -
  4972.                             LBMin := LowerBoundParts1
  4973.                             LBMax := LowerBoundParts2
  4974.                             UBMin := UpperBoundParts1
  4975.                             UBMax := UpperBoundParts2
  4976.                             If (Opts.CompactDoubleRanges)
  4977.                             {
  4978.                                 ValueRange := StrPad(LBMin . "-" . UBMax, Opts.ValueRangeFieldWidth, "left")
  4979.                             }
  4980.                             Else
  4981.                             {
  4982.                                 ValueRange := StrPad(LowerBound, Opts.ValueRangeFieldWidth, "left") . Opts.AffixDetailDelimiter . StrPad(UpperBound, Opts.ValueRangeFieldWidth, "left")
  4983.                             }
  4984.                         }
  4985.                         ProcessedLine := AffixLine . Delim . StrPad(ValueRange, Opts.ValueRangeFieldWidth, "left")
  4986.                         If (AppendImplicitSep)
  4987.                         {
  4988.                             ProcessedLine := ProcessedLine . "`n" . "--------"
  4989.                             AppendImplicitSep := False
  4990.                         }
  4991.                         AffixLines.Set(Idx, ProcessedLine)
  4992.                     }
  4993.                     Else
  4994.                     {
  4995.                         AffixLines.Set(Idx, CurLinePart)
  4996.                     }
  4997.                     Idx += 1
  4998.                 }
  4999.             }
  5000.             return UniqueFound
  5001.         }
  5002.     }
  5003.     return UniqueFound
  5004. }
  5005.  
  5006. ItemIsMirrored(ItemDataText)
  5007. {
  5008.     Loop, Parse, ItemDataText, `n, `r
  5009.     {
  5010.         If (A_LoopField == "Mirrored")
  5011.         {
  5012.             return True
  5013.         }
  5014.     }
  5015.     return False
  5016. }
  5017.  
  5018. ; ########### MAIN PARSE FUNCTION ##############
  5019.  
  5020. ; Invocation stack (simplified) for full item parse:
  5021. ;
  5022. ;   (timer watches clipboard contents)
  5023. ;   (on clipboard changed) ->
  5024. ;
  5025. ;   ParseClipBoardChanges()
  5026. ;       PreProcessContents()
  5027. ;       ParseItemData()
  5028. ;           (get item details by calling many other Parse... functions)
  5029. ;           ParseAffixes()
  5030. ;               (on affix match found) ->
  5031. ;                   LookupAffixData()
  5032. ;                       AssembleValueRangeFields()
  5033. ;                   LookupAffixBracket()
  5034. ;                   LookupRemainingAffixBracket()
  5035. ;                   AppendAffixInfo(MakeAffixDetailLine()) ; appends to global AffixLines table
  5036. ;           (is Weapon) ->
  5037. ;               AssembleDamageDetails()
  5038. ;           AssembleAffixDetails() ; uses global AffixLines table
  5039. ;       PostProcessData()
  5040. ;       ShowToolTip()
  5041. ;
  5042. ParseItemData(ItemDataText, ByRef RarityLevel="")
  5043. {    
  5044.     Global Item, ItemData, AffixTotals, uniqueMapList, mapList, matchList
  5045.  
  5046.     ItemDataPartsIndexLast =
  5047.     ItemDataPartsIndexAffixes =
  5048.     ItemDataPartsLast =
  5049.     ItemDataNamePlate =
  5050.     ItemDataStats =
  5051.     ItemDataAffixes =
  5052.     ItemDataRequirements =
  5053.     ItemDataRarity =
  5054.     ItemDataLinks =
  5055.     ItemName =
  5056.     ItemTypeName =
  5057.     ItemQuality =
  5058.     ItemLevel =
  5059.     ItemMaxSockets =
  5060.     ItemBaseType =
  5061.     ItemSubType =
  5062.     ItemGripType =
  5063.     BaseLevel =
  5064.     RarityLevel =  
  5065.     TempResult =
  5066.  
  5067.     Item.IsWeapon := False
  5068.     Item.IsQuiver := False
  5069.     Item.IsFlask := False
  5070.     Item.IsGem := False
  5071.     Item.IsCurrency := False
  5072.     Item.IsUnidentified := False
  5073.     Item.IsBelt := False
  5074.     Item.IsRing := False
  5075.     Item.IsUnsetRing := False
  5076.     Item.IsBow := False
  5077.     Item.IsAmulet := False
  5078.     Item.IsSingleSocket := False
  5079.     Item.IsFourSocket := False  
  5080.     Item.IsThreeSocket := False
  5081.     Item.IsMap := False
  5082.     Item.IsUnique := False
  5083.     Item.IsRare := False
  5084.     Item.IsCorrupted := False
  5085.     Item.IsMirrored := False
  5086.     Item.HasEffect := False
  5087.    
  5088.     ResetAffixDetailVars()
  5089.    
  5090.     ItemData.FullText := ItemDataText
  5091.  
  5092.     IfInString, ItemDataText, Corrupted
  5093.     {
  5094.         Item.IsCorrupted := True
  5095.     }
  5096.    
  5097.     ; AHK only allows splitting on single chars, so first
  5098.     ; replace the split string (\r\n--------\r\n) with AHK's escape char (`)
  5099.     ; then do the actual string splitting...
  5100.     StringReplace, TempResult, ItemDataText, `r`n--------`r`n, ``, All
  5101.     StringSplit, ItemDataParts, TempResult, ``,
  5102.  
  5103.     ItemData.NamePlate := ItemDataParts1
  5104.     ItemData.Stats := ItemDataParts2
  5105.    
  5106.     ItemDataIndexLast := ItemDataParts0
  5107.     ItemDataPartsLast := ItemDataParts%ItemDataIndexLast%
  5108.     ItemData.ClearParts()
  5109.     Loop, %ItemDataParts0%
  5110.     {
  5111.         ItemData.Parts[A_Index] := ItemDataParts%A_Index%
  5112.     }
  5113.     ItemData.PartsLast := ItemDataPartsLast
  5114.     ItemData.IndexLast := ItemDataIndexLast
  5115.    
  5116.     ; ItemData.Requirements := GetItemDataChunk(ItemDataText, "Requirements:")
  5117.     ; ParseRequirements(ItemData.Requirements, RequiredLevel, RequiredAttributes, RequiredAttributeValues)
  5118.  
  5119.     ParseItemName(ItemData.NamePlate, ItemName, ItemTypeName)
  5120.     If (Not ItemName)
  5121.     {
  5122.         return
  5123.     }
  5124.     Item.Name := ItemName
  5125.     Item.TypeName := ItemTypeName
  5126.  
  5127.     IfInString, ItemDataText, Unidentified
  5128.     {
  5129.         If (Item.Name != "Scroll of Wisdom")
  5130.         {
  5131.             Item.IsUnidentified := True
  5132.         }
  5133.     }
  5134.  
  5135.     Item.Quality := ParseQuality(ItemData.Stats)
  5136.    
  5137.     ; This function should return the second part of the "Rarity: ..." line
  5138.     ; in the case of "Rarity: Unique" it should return "Unique"
  5139.     ItemData.Rarity := ParseRarity(ItemData.NamePlate)
  5140.  
  5141.     ItemData.Links := ParseLinks(ItemDataText)
  5142.  
  5143.     Item.IsUnique := False
  5144.     If (InStr(ItemData.Rarity, "Unique"))
  5145.     {
  5146.         Item.IsUnique := True
  5147.     }
  5148.  
  5149.     If (InStr(ItemData.Rarity, "Rare"))
  5150.     {
  5151.         Item.IsRare := True
  5152.     }
  5153.  
  5154.     Item.IsGem := (InStr(ItemData.Rarity, "Gem"))
  5155.     Item.IsCurrency := (InStr(ItemData.Rarity, "Currency"))
  5156.    
  5157.     If (Not (InStr(ItemDataText, "Itemlevel:") or InStr(ItemDataText, "Item Level:")) and Not Item.IsGem and Not Item.IsCurrency)
  5158.     {
  5159.         return Item.Name
  5160.     }
  5161.    
  5162.     If (Item.IsGem)
  5163.     {
  5164.         RarityLevel := 0
  5165.         Item.Level := ParseGemLevel(ItemDataText, "Level:")
  5166.         ItemLevelWord := "Gem Level:"
  5167.         Item.BaseType := "Jewelry"
  5168.     }
  5169.     Else
  5170.     {
  5171.         If (Item.IsCurrency and Opts.ShowCurrencyValueInChaos == 1)
  5172.         {
  5173.             ValueInChaos := ConvertCurrency(Item.Name, ItemData.Stats)
  5174.             If (ValueInChaos)
  5175.             {
  5176.                 CurrencyDetails := ValueInChaos . " Chaos"
  5177.             }
  5178.         }
  5179.         Else If (Not Item.IsCurrency)
  5180.         {
  5181.             RarityLevel := CheckRarityLevel(ItemData.Rarity)
  5182.             Item.Level := ParseItemLevel(ItemDataText)
  5183.             ItemLevelWord := "Item Level:"
  5184.             ParseItemType(ItemData.Stats, ItemData.NamePlate, ItemBaseType, ItemSubType, ItemGripType)
  5185.             Item.BaseType := ItemBaseType
  5186.             Item.SubType := ItemSubType
  5187.             Item.GripType := ItemGripType
  5188.         }
  5189.     }
  5190.    
  5191.     Item.RarityLevel := RarityLevel
  5192.  
  5193.     Item.IsBow := (Item.SubType == "Bow")
  5194.     Item.IsFlask := (Item.SubType == "Flask")
  5195.     Item.IsBelt := (Item.SubType == "Belt")
  5196.     Item.IsRing := (Item.SubType == "Ring")
  5197.     Item.IsUnsetRing := (Item.IsRing and InStr(ItemData.NamePlate, "Unset Ring"))
  5198.     Item.IsAmulet := (Item.SubType == "Amulet")
  5199.     Item.IsSingleSocket := (IsUnsetRing)
  5200.     Item.IsFourSocket := (Item.SubType == "Gloves" or Item.SubType == "Boots" or Item.SubType == "Helmet")
  5201.     Item.IsThreeSocket := ((Item.GripType == "1H" or Item.SubType == "Shield") and Not Item.IsBow)
  5202.     Item.IsQuiver := (Item.SubType == "Quiver")
  5203.     Item.IsWeapon := (Item.BaseType == "Weapon")
  5204.     Item.IsMap := (Item.BaseType == "Map")
  5205.     Item.IsMirrored := (ItemIsMirrored(ItemDataText) and Not Item.IsCurrency)
  5206.     Item.HasEffect := (InStr(ItemData.PartsLast, "Has"))
  5207.    
  5208.     ItemDataIndexAffixes := ItemData.IndexLast - GetNegativeAffixOffset(Item)
  5209.     If (ItemDataIndexAffixes <= 0)
  5210.     {
  5211.         ; ItemDataParts doesn't have the parts/text we need. Bail.
  5212.         ; This might be because the clipboard is completely empty.
  5213.         return
  5214.     }
  5215.     ItemData.Affixes := ItemDataParts%ItemDataIndexAffixes%
  5216.     ItemData.IndexAffixes := ItemDataIndexAffixes
  5217.    
  5218.     ItemData.Stats := ItemDataParts2
  5219.  
  5220.     If (Item.IsFlask)
  5221.     {
  5222.         ParseFlaskAffixes(ItemData.Affixes)
  5223.     }
  5224.     Else If (RarityLevel > 1 and RarityLevel < 4)
  5225.     {
  5226.         ParseAffixes(ItemData.Affixes, Item)
  5227.     }
  5228.     NumPrefixes := AffixTotals.NumPrefixes
  5229.     NumSuffixes := AffixTotals.NumSuffixes
  5230.     TotalAffixes := NumPrefixes + NumSuffixes
  5231.     AffixTotals.NumTotals := TotalAffixes
  5232.  
  5233.     ; Start assembling the text for the tooltip
  5234.     TT := Item.Name
  5235.     If (Item.TypeName)
  5236.     {
  5237.         TT := TT . "`n" . Item.TypeName
  5238.     }
  5239.    
  5240.     If (Item.IsCurrency)
  5241.     {
  5242.         TT := TT . "`n" . CurrencyDetails
  5243.         Goto, ParseItemDataEnd
  5244.     }
  5245.  
  5246.     If (Opts.ShowItemLevel == 1 and Not (Item.IsMap or Item.IsCurrency))
  5247.     {
  5248.         TT := TT . "`n"
  5249.         TT := TT . ItemLevelWord . "   " . StrPad(Item.Level, 3, Side="left")
  5250.         If (Not Item.IsFlask)
  5251.         {
  5252.             Item.BaseLevel := CheckBaseLevel(Item.TypeName)
  5253.             If (Item.BaseLevel)
  5254.             {
  5255.                 TT := TT . "`n" . "Base Level:   " . StrPad(Item.BaseLevel, 3, Side="left")
  5256.             }
  5257.         }
  5258.     }
  5259.    
  5260.     If (Opts.ShowMaxSockets == 1 and Not (Item.IsFlask or Item.IsGem or Item.IsCurrency or Item.IsBelt or Item.IsQuiver or Item.IsMap or Item.IsAmulet))
  5261.     {
  5262.         If (Item.Level >= 50)
  5263.         {
  5264.             Item.MaxSockets := 6
  5265.         }
  5266.         Else If (Item.Level >= 35)
  5267.         {
  5268.             Item.MaxSockets := 5
  5269.         }
  5270.         Else If (Item.Level >= 28)
  5271.         {
  5272.             Item.MaxSockets := 4
  5273.         }
  5274.         Else If (Item.Level >= 15)
  5275.         {
  5276.             Item.MaxSockets := 3
  5277.         }
  5278.         Else
  5279.         {
  5280.             Item.MaxSockets := 2
  5281.         }
  5282.        
  5283.         If(Item.IsFourSocket and Item.MaxSockets > 4)
  5284.         {
  5285.             Item.MaxSockets := 4
  5286.         }
  5287.         Else If(Item.IsThreeSocket and Item.MaxSockets > 3)
  5288.         {
  5289.             Item.MaxSockets := 3
  5290.         }
  5291.         Else If(Item.IsSingleSocket)
  5292.         {
  5293.             Item.MaxSockets := 1
  5294.         }
  5295.  
  5296.         If (Not Item.IsRing or Item.IsUnsetRing)
  5297.         {
  5298.             TT := TT . "`n"
  5299.             TT := TT . "Max Sockets:    "
  5300.             TT := TT . Item.MaxSockets
  5301.         }
  5302.     }
  5303.    
  5304.     If (Opts.ShowGemEvaluation == 1 and Item.IsGem)
  5305.     {
  5306.         SepAdded := False
  5307.         If (Item.Quality > 0)
  5308.         {
  5309.             TT = %TT%`n--------
  5310.             SepAdded := True
  5311.             TT := TT . "`n" . "+" . Item.Quality . "`%"
  5312.         }
  5313.         If (Item.Quality >= Opts.GemQualityValueThreshold or GemIsValuable(Item.Name))
  5314.         {
  5315.             If (Not SepAdded)
  5316.             {
  5317.                 TT = %TT%`n--------
  5318.                 SepAdded := True
  5319.             }
  5320.             TT = %TT%`nValuable
  5321.         }
  5322.         If (GemIsDropOnly(Item.Name))
  5323.         {
  5324.             If (Not SepAdded)
  5325.             {
  5326.                 TT = %TT%`n--------
  5327.                 SepAdded := True
  5328.             }
  5329.             TT = %TT%`nDrop Only
  5330.         }
  5331.     }
  5332.  
  5333.     If (Opts.ShowDamageCalculations == 1 and Item.IsWeapon)
  5334.     {
  5335.         TT := TT . AssembleDamageDetails(ItemDataText)
  5336.     }
  5337.  
  5338.     If (Item.IsMap)
  5339.     {
  5340.         If (Item.IsUnique)
  5341.         {
  5342.             MapDescription := uniqueMapList[Item.SubType]
  5343.         }
  5344.         Else
  5345.         {
  5346.             MapDescription := mapList[Item.SubType]
  5347.         }
  5348.  
  5349.         TT = %TT%`n%MapDescription%
  5350.     }
  5351.    
  5352.     If (RarityLevel > 1 and RarityLevel < 4)
  5353.     {
  5354.         ; Append affix info if rarity is greater than normal (white)
  5355.         ; Affix total statistic
  5356.         If (Opts.ShowAffixTotals = 1)
  5357.         {
  5358.             If (NumPrefixes = 1)
  5359.             {
  5360.                 WordPrefixes = Prefix
  5361.             }
  5362.             Else
  5363.             {
  5364.                 WordPrefixes = Prefixes
  5365.             }
  5366.             If (NumSuffixes = 1)
  5367.             {
  5368.                 WordSuffixes = Suffix
  5369.             }
  5370.             Else
  5371.             {
  5372.                 WordSuffixes = Suffixes
  5373.             }
  5374.  
  5375.             PrefixLine =
  5376.             If (NumPrefixes > 0)
  5377.             {
  5378.                 PrefixLine = `n   %NumPrefixes% %WordPrefixes%
  5379.             }
  5380.  
  5381.             SuffixLine =
  5382.             If (NumSuffixes > 0)
  5383.             {
  5384.                 SuffixLine = `n   %NumSuffixes% %WordSuffixes%
  5385.             }
  5386.  
  5387.             AffixStats =
  5388.             If (TotalAffixes > 0 and Not Item.IsUnidentified)
  5389.             {
  5390.                 AffixStats = Affixes (%TotalAffixes%):%PrefixLine%%SuffixLine%
  5391.                 TT = %TT%`n--------`n%AffixStats%
  5392.             }
  5393.         }
  5394.        
  5395.         ; Detailed affix range infos
  5396.         If (Opts.ShowAffixDetails == 1)
  5397.         {
  5398.             If (Not Item.IsFlask and Not Item.IsUnidentified and Not Item.IsMap)
  5399.             {
  5400.                 AffixDetails := AssembleAffixDetails()
  5401.                 TT = %TT%`n--------%AffixDetails%
  5402.            }
  5403.         }
  5404.     }
  5405.     Else If (ItemData.Rarity == "Unique")
  5406.     {
  5407.         If (FindUnique(Item.Name) == False and Not Item.IsUnidentified)
  5408.         {
  5409.             TT = %TT%`n--------`nUnique item currently not supported
  5410.         }
  5411.         Else If (Opts.ShowAffixDetails == True and Not Item.IsUnidentified)
  5412.         {
  5413.             ParseUnique(Item.Name)
  5414.             AffixDetails := AssembleAffixDetails()
  5415.             TT = %TT%`n--------%AffixDetails%
  5416.         }
  5417.     }
  5418.    
  5419.     If (Item.IsUnidentified and (Item.Name != "Scroll of Wisdom") and Not Item.IsMap)
  5420.     {
  5421.         TT = %TT%`n--------`nUnidentified
  5422.     }
  5423.  
  5424.     If ((Item.IsUnique and (Opts.ShowUniqueEvaluation == 1) and UniqueIsValuable(Item.Name)) or (Opts.MarkHighLinksAsValuable == 1 and (Item.IsUnique or Item.IsRare) and ItemData.Links >= 5))
  5425.     {
  5426.         TT = %TT%`n--------`nValuable
  5427.     }
  5428.  
  5429.     If (Item.IsMirrored)
  5430.     {
  5431.         TT = %TT%`n--------`nMirrored
  5432.     }
  5433.  
  5434.     return TT
  5435.    
  5436.     ParseItemDataEnd:
  5437.         return TT
  5438. }
  5439.  
  5440. GetNegativeAffixOffset(Item)
  5441. {
  5442.     NegativeAffixOffset := 0
  5443.     If (Item.IsFlask or Item.IsUnique)
  5444.     {
  5445.         ; Uniques as well as flasks have descriptive text as last item,
  5446.         ; so decrement item index to get to the item before last one
  5447.         NegativeAffixOffset := NegativeAffixOffset + 1
  5448.     }
  5449.     If (Item.HasEffect)
  5450.     {
  5451.         ; Same with weapon skins or other effects
  5452.         NegativeAffixOffset := NegativeAffixOffset + 1
  5453.     }
  5454.     If (Item.IsCorrupted)
  5455.     {
  5456.         ; And corrupted items
  5457.         NegativeAffixOffset := NegativeAffixOffset + 1
  5458.     }
  5459.     If (Item.IsMirrored)
  5460.     {
  5461.         ; And mirrored items
  5462.         NegativeAffixOffset := NegativeAffixOffset + 1
  5463.     }
  5464.     return NegativeAffixOffset
  5465. }
  5466.  
  5467. ; Don't use! Not working correctly yet!
  5468. ExtractRareItemTypeName(ItemName)
  5469. {
  5470.     ItemTypeName := RegExReplace(ItemName, "(.+?) (.+) of (.+)", "$2")
  5471.     return ItemTypeName
  5472. }
  5473.  
  5474. ; Show tooltip, with fixed width font
  5475. ShowToolTip(String)
  5476. {
  5477.     Global X, Y, ToolTipTimeout, Opts
  5478.    
  5479.     ; Get position of mouse cursor
  5480.     MouseGetPos, X, Y
  5481.  
  5482.     If (Not Opts.DisplayToolTipAtFixedCoords)
  5483.     {
  5484.         ToolTip, %String%, X - 135, Y + 35
  5485.         Fonts.SetFixedFont()
  5486.         ToolTip, %String%, X - 135, Y + 35
  5487.     }
  5488.     Else
  5489.     {
  5490.         CoordMode, ToolTip, Screen
  5491.         ;~ GetScreenInfo()
  5492.         ;~ TotalScreenWidth := Globals.Get("TotalScreenWidth", 0)
  5493.         ;~ HalfWidth := Round(TotalScreenWidth / 2)
  5494.        
  5495.         ;~ SecondMonitorTopLeftX := HalfWidth
  5496.         ;~ SecondMonitorTopLeftY := 0
  5497.         ScreenOffsetY := Opts.ScreenOffsetY
  5498.         ScreenOffsetX := Opts.ScreenOffsetX
  5499.        
  5500.         XCoord := 0 + ScreenOffsetX
  5501.         YCoord := 0 + ScreenOffsetY
  5502.        
  5503.         ToolTip, %String%, XCoord, YCoord
  5504.         Fonts.SetFixedFont()
  5505.         ToolTip, %String%, XCoord, YCoord
  5506.     }    
  5507.     ;Fonts.SetFixedFont()
  5508.    
  5509.     ; Set up count variable and start timer for tooltip timeout
  5510.     ToolTipTimeout := 0
  5511.     SetTimer, ToolTipTimer, 100
  5512. }
  5513.  
  5514. ; ############## TESTS #################
  5515.  
  5516. Globals.Set("TestCaseSeparator", "####################")
  5517.  
  5518. RunRareTestSuite(Path, SuiteNumber)
  5519. {
  5520.     Global AffixTotals
  5521.    
  5522.     NumTestCases := 0
  5523.     Loop, Read, %Path%
  5524.     {  
  5525.         IfInString, A_LoopReadLine, % Globals.TestCaseSeparator
  5526.         {
  5527.             NumTestCases += 1
  5528.             Continue
  5529.         }
  5530.         TestCaseText := A_LoopReadLine
  5531.         TestCases%NumTestCases% := TestCases%NumTestCases% . TestCaseText . "`r`n"
  5532.     }
  5533.  
  5534.     Failures := 0
  5535.     Successes := 0
  5536.     FailureNumbers =
  5537.     TestCase =
  5538.     Loop, %NumTestCases%
  5539.     {
  5540.         TestCase := TestCases%A_Index%
  5541.  
  5542.         RarityLevel := 0
  5543.         TestCaseResult := ParseItemData(TestCase, RarityLevel)
  5544.         NumPrefixes := AffixTotals.NumPrefixes
  5545.         NumSuffixes := AffixTotals.NumSuffixes
  5546.  
  5547.         StringReplace, TempResult, TestCaseResult, --------, ``, All  
  5548.         StringSplit, TestCaseResultParts, TempResult, ``
  5549.  
  5550.         NameAndDPSPart := TestCaseResultParts1
  5551.         TotalAffixStatsPart := TestCaseResultParts2
  5552.         AffixCompositionPart := TestCaseResultParts3
  5553.  
  5554.         ; failure conditions
  5555.         TotalAffixes := 0
  5556.         TotalAffixes := NumPrefixes + NumSuffixes
  5557.         InvalidTotalAffixNumber := (TotalAffixes > 6)
  5558.         BracketLookupFailed := InStr(TestCaseResult, "n/a")
  5559.         CompositeRangeCalcFailed := InStr(TestCaseResult, " - ")
  5560.  
  5561.         Prefixes := 0
  5562.         Suffixes := 0
  5563.         CompPrefixes := 0
  5564.         CompSuffixes := 0
  5565.         ExtractTotalAffixBalance(AffixCompositionPart, Prefixes, Suffixes, CompPrefixes, CompSuffixes)
  5566.  
  5567.         HasDanglingComposites := False
  5568.         If (Mod(CompPrefixes, 2)) ; True, if not evenly divisible by 2
  5569.         {
  5570.             HasDanglingComposites := True
  5571.         }
  5572.         If (Mod(CompSuffixes, 2))
  5573.         {
  5574.             HasDanglingComposites := True
  5575.         }
  5576.  
  5577.         TotalCountByAffixTypes := (Floor(CompPrefixes / 2) + Floor(CompSuffixes / 2) + Prefixes + Suffixes)
  5578.  
  5579.         AffixTypesCountedIncorrectly := (Not (TotalCountByAffixTypes == TotalAffixes))
  5580.         If (InvalidTotalAffixNumber or BracketLookupFailed or CompositeRangeCalcFailed or HasDanglingComposites or AffixTypesCountedIncorrectly)
  5581.         {
  5582.             Failures += 1
  5583.             FailureNumbers := FailureNumbers . A_Index . ","
  5584.         }
  5585.         Else
  5586.         {
  5587.             Successes += 1
  5588.         }
  5589.         ; needed so global variables can be yanked from memory and reset between calls
  5590.         ; (if you reload the script really fast globals vars that are out of date can
  5591.         ; cause failures when there are none)
  5592.         Sleep, 1
  5593.     }
  5594.  
  5595.     Result := "Suite " . SuiteNumber . ": " . StrPad(Successes, 5, "left") . " OK" . ", " . StrPad(Failures, 5, "left")  . " Failed"
  5596.     If (Failures > 0)
  5597.     {
  5598.         FailureNumbers := SubStr(FailureNumbers, 1, -1)
  5599.         Result := Result . " (" . FailureNumbers . ")"
  5600.     }
  5601.     return Result
  5602. }
  5603.  
  5604. RunUniqueTestSuite(Path, SuiteNumber)
  5605. {
  5606.     Global AffixTotals
  5607.    
  5608.     NumTestCases := 0
  5609.     Loop, Read, %Path%
  5610.     {  
  5611.         IfInString, A_LoopReadLine, % Globals.TestCaseSeparator
  5612.         {
  5613.             NumTestCases += 1
  5614.             Continue
  5615.         }
  5616.         TestCaseText := A_LoopReadLine
  5617.         TestCases%NumTestCases% := TestCases%NumTestCases% . TestCaseText . "`r`n"
  5618.     }
  5619.  
  5620.     Failures := 0
  5621.     Successes := 0
  5622.     FailureNumbers =
  5623.     TestCase =
  5624.     Loop, %NumTestCases%
  5625.     {
  5626.         TestCase := TestCases%A_Index%
  5627.         TestCaseResult := ParseItemData(TestCase)
  5628.  
  5629.         FailedToSepImplicit := InStr(TestCaseResult, "@")  ; failed to properly seperate implicit from normal affixes
  5630.         ; TODO: add more unique item test failure conditions
  5631.  
  5632.         If (FailedToSepImplicit)
  5633.         {
  5634.             Failures += 1
  5635.             FailureNumbers := FailureNumbers . A_Index . ","
  5636.         }
  5637.         Else
  5638.         {
  5639.             Successes += 1
  5640.         }
  5641.         ; needed so global variables can be yanked from memory and reset between calls
  5642.         ; (if you reload the script really fast globals vars that are out of date can
  5643.         ; cause failures where there are none)
  5644.         Sleep, 1
  5645.     }
  5646.  
  5647.     Result := "Suite " . SuiteNumber . ": " . StrPad(Successes, 5, "left") . " OK" . ", " . StrPad(Failures, 5, "left")  . " Failed"
  5648.     If (Failures > 0)
  5649.     {
  5650.         FailureNumbers := SubStr(FailureNumbers, 1, -1)
  5651.         Result := Result . " (" . FailureNumbers . ")"
  5652.     }
  5653.     return Result
  5654. }
  5655.  
  5656. RunAllTests()
  5657. {
  5658.     ; change this to the number of available test suites
  5659.     TestDataBasePath = %A_ScriptDir%\extras\tests
  5660.  
  5661.     NumRareTestSuites := 5
  5662.     RareResults := "Rare Items"
  5663.     Loop, %NumRareTestSuites%
  5664.     {
  5665.         If (A_Index > 0) ; change condition to only run certain tests
  5666.         {
  5667.             TestSuitePath = %TestDataBasePath%\Rares%A_Index%.txt
  5668.             TestSuiteResult := RunRareTestSuite(TestSuitePath, A_Index)
  5669.             RareResults := RareResults . "`n    " . TestSuiteResult
  5670.         }
  5671.     }
  5672.  
  5673.     NumUniqueTestSuites := 1
  5674.     UniqResults := "Unique Items"
  5675.     Loop, %NumUniqueTestSuites%
  5676.     {
  5677.         If (A_Index > 0) ; change condition to only run certain tests
  5678.         {
  5679.             TestSuitePath = %TestDataBasePath%\Uniques%A_Index%.txt
  5680.             TestSuiteResult := RunUniqueTestSuite(TestSuitePath, A_Index)
  5681.             UniqResults := UniqResults . "`n    " . TestSuiteResult
  5682.         }
  5683.     }
  5684.  
  5685.     MsgBox, %RareResults%`n`n%UniqResults%
  5686. }
  5687.  
  5688. ; ########### TESTS ############
  5689.  
  5690. If (RunTests)
  5691. {
  5692.     RunAllTests()
  5693. }
  5694.  
  5695. ; ############ GUI #############
  5696.  
  5697. GuiSet(ControlID, Param3="", SubCmd="")
  5698. {
  5699.     If (!(SubCmd == "")) {
  5700.         GuiControl, %SubCmd%, %ControlID%, %Param3%
  5701.     } Else {
  5702.         GuiControl,, %ControlID%, %Param3%
  5703.     }
  5704. }
  5705.  
  5706. GuiGet(ControlID, DefaultValue="")
  5707. {
  5708.     curVal =
  5709.     GuiControlGet, curVal,, %ControlID%, %DefaultValue%
  5710.     return curVal
  5711. }
  5712.  
  5713. GuiAdd(ControlType, Contents, PositionInfo, AssocVar="", AssocHwnd="", AssocLabel="", Param4="")
  5714. {
  5715.     Global
  5716.     Local av, ah, al
  5717.     av := StrPrefix(AssocVar, "v")
  5718.     al := StrPrefix(AssocLabel, "g")
  5719.     ah := StrPrefix(AssocHwnd, "hwnd")
  5720.     Gui, Add, %ControlType%, %PositionInfo% %av% %al% %ah% %Param4%, %Contents%
  5721. }
  5722.  
  5723. GuiAddButton(Contents, PositionInfo, AssocLabel="", AssocVar="", AssocHwnd="", Options="")
  5724. {
  5725.     GuiAdd("Button", Contents, PositionInfo, AssocVar, AssocHwnd, AssocLabel, Options)
  5726. }
  5727.  
  5728. GuiAddGroupBox(Contents, PositionInfo, AssocVar="", AssocHwnd="", AssocLabel="", Options="")
  5729. {
  5730.     GuiAdd("GroupBox", Contents, PositionInfo, AssocVar, AssocHwnd, AssocLabel, Options)
  5731. }
  5732.  
  5733. GuiAddCheckbox(Contents, PositionInfo, CheckedState=0, AssocVar="", AssocHwnd="", AssocLabel="", Options="")
  5734. {
  5735.     GuiAdd("Checkbox", Contents, PositionInfo, AssocVar, AssocHwnd, AssocLabel, "Checked" . CheckedState . " " . Options)
  5736. }
  5737.  
  5738. GuiAddText(Contents, PositionInfo, AssocVar="", AssocHwnd="", AssocLabel="", Options="")
  5739. {
  5740.     GuiAdd("Text", Contents, PositionInfo, AssocVar, AssocHwnd, AssocLabel, Options)
  5741. }
  5742.  
  5743. GuiAddEdit(Contents, PositionInfo, AssocVar="", AssocHwnd="", AssocLabel="", Options="")
  5744. {
  5745.     GuiAdd("Edit", Contents, PositionInfo, AssocVar, AssocHwnd, AssocLabel, Options)
  5746. }
  5747.  
  5748. AddToolTip(con, text, Modify=0){
  5749.     Static TThwnd, GuiHwnd
  5750.     TInfo =
  5751.     UInt := "UInt"
  5752.     Ptr := (A_PtrSize ? "Ptr" : UInt)
  5753.     PtrSize := (A_PtrSize ? A_PtrSize : 4)
  5754.     Str := "Str"
  5755.     ; defines from Windows MFC commctrl.h
  5756.     WM_USER := 0x400
  5757.     TTM_ADDTOOL := (A_IsUnicode ? WM_USER+50 : WM_USER+4)           ; used to add a tool, and assign it to a control
  5758.     TTM_UPDATETIPTEXT := (A_IsUnicode ? WM_USER+57 : WM_USER+12)    ; used to adjust the text of a tip
  5759.     TTM_SETMAXTIPWIDTH := WM_USER+24                                ; allows the use of multiline tooltips
  5760.     TTF_IDISHWND := 1
  5761.     TTF_CENTERTIP := 2
  5762.     TTF_RTLREADING := 4
  5763.     TTF_SUBCLASS := 16
  5764.     TTF_TRACK := 0x0020
  5765.     TTF_ABSOLUTE := 0x0080
  5766.     TTF_TRANSPARENT := 0x0100
  5767.     TTF_PARSELINKS := 0x1000
  5768.     If (!TThwnd) {
  5769.         Gui, +LastFound
  5770.         GuiHwnd := WinExist()
  5771.         TThwnd := DllCall("CreateWindowEx"
  5772.                     ,UInt,0
  5773.                     ,Str,"tooltips_class32"
  5774.                     ,UInt,0
  5775.                     ,UInt,2147483648
  5776.                     ,UInt,-2147483648
  5777.                     ,UInt,-2147483648
  5778.                     ,UInt,-2147483648
  5779.                     ,UInt,-2147483648
  5780.                     ,UInt,GuiHwnd
  5781.                     ,UInt,0
  5782.                     ,UInt,0
  5783.                     ,UInt,0)
  5784.     }
  5785.     ; TOOLINFO structure
  5786.     cbSize := 6*4+6*PtrSize
  5787.     uFlags := TTF_IDISHWND|TTF_SUBCLASS|TTF_PARSELINKS
  5788.     VarSetCapacity(TInfo, cbSize, 0)
  5789.     NumPut(cbSize, TInfo)
  5790.     NumPut(uFlags, TInfo, 4)
  5791.     NumPut(GuiHwnd, TInfo, 8)
  5792.     NumPut(con, TInfo, 8+PtrSize)
  5793.     NumPut(&text, TInfo, 6*4+3*PtrSize)
  5794.     NumPut(0,TInfo, 6*4+6*PtrSize)
  5795.     DetectHiddenWindows, On
  5796.     If (!Modify) {
  5797.         DllCall("SendMessage"
  5798.             ,Ptr,TThwnd
  5799.             ,UInt,TTM_ADDTOOL
  5800.             ,Ptr,0
  5801.             ,Ptr,&TInfo
  5802.             ,Ptr)
  5803.         DllCall("SendMessage"
  5804.             ,Ptr,TThwnd
  5805.             ,UInt,TTM_SETMAXTIPWIDTH
  5806.             ,Ptr,0
  5807.             ,Ptr,A_ScreenWidth)
  5808.     }
  5809.     DllCall("SendMessage"
  5810.         ,Ptr,TThwnd
  5811.         ,UInt,TTM_UPDATETIPTEXT
  5812.         ,Ptr,0
  5813.         ,Ptr,&TInfo
  5814.         ,Ptr)
  5815.  
  5816. }
  5817.  
  5818. GetScreenInfo()
  5819. {
  5820.     SysGet, TotalScreenWidth, 78
  5821.     SysGet, TotalscreenHeight, 79
  5822.     SysGet, MonitorCount, 80
  5823.    
  5824.     Globals.Set("MonitorCount", MonitorCount)
  5825.     Globals.Set("TotalScreenWidth", TotalScreenWidth)
  5826.     Globals.Set("TotalScreenHeight", TotalscreenHeight)    
  5827. }
  5828.  
  5829. ; ######### UNHANDLED CASE DIALOG ############
  5830.  
  5831. ShowUnhandledCaseDialog()
  5832. {
  5833.     Global Msg, Globals
  5834.     Static UnhDlg_EditItemText
  5835.    
  5836.     Gui, 3:New,, Unhandled Case
  5837.     Gui, 3:Color, FFFFFF
  5838.     Gui, 3:Add, Picture, x25 y25 w36 h36, %A_ScriptDir%\data\info.png
  5839.     Gui, 3:Add, Text, x65 y31 w500 h100, % Msg.Unhandled
  5840.     Gui, 3:Add, Edit, x65 y96 w400 h120 ReadOnly vUnhDlg_EditItemText, % Globals.Get("ItemText", "Error: could'nt get item text (system clipboard modified?). Please try again or report the item manually.")
  5841.     Gui, 3:Add, Text, x-5 y230 w500 h50 -Background
  5842.     Gui, 3:Add, Button, x195 y245 w100 h25 gUnhandledDlg_ShowItemText, Show In Notepad
  5843.     Gui, 3:Add, Button, x300 y245 w90 h25 gVisitForumsThread, Forums Thread
  5844.     Gui, 3:Add, Button, x395 y245 w86 h25 gUnhandledDlg_OK Default, OK
  5845.     Gui, 3:Show, Center w490 h280,
  5846.     Gui, Font, s10, Courier New
  5847.     Gui, Font, s9, Consolas  
  5848.     GuiControl, Font, UnhDlg_EditItemText
  5849.     return
  5850. }
  5851.  
  5852. ; ######### SETTINGS ############
  5853.  
  5854. ; (Internal: RegExr x-forms)
  5855. ; GroupBox
  5856. ;   Gui, Add, GroupBox, (.+?) , (.+) -> ; $2 \n\n    GuiAddGroupBox("$2", "$1")
  5857. ; Checkbox (with label)
  5858. ;   Gui, Add, (.+?), (.+?) hwnd(.+?) v(.+?) g(.+?) Checked%(.+)%, (.+) -> GuiAdd$1("$7", "$2", Opts.$6, "$4", "$3", "$5")
  5859. ; Checkbox /w/o label)
  5860. ;   Gui, Add, (.+?), (.+?) hwnd(.+?) v(.+?) Checked%(.+)%, (.+) -> GuiAdd$1("$6", "$2", Opts.$5, "$4", "$3")
  5861. ; Edit
  5862. ;   Gui, Add, Edit, (.+?) hwnd(.+?) v(.+?), %(.+)% -> GuiAddEdit(Opts.$4, "$1", "$3", "", "$2")
  5863. ; Text
  5864. ;   Gui, Add, Text, (.+?) hwnd(.+?) v(.+?), (.+) -> GuiAddText("$4", "$1", "$3", "", "$2")
  5865. ; Button
  5866. ;   Gui, Add, Button, (.+?) g(.+?), (.+) -> GuiAddButton("$3", "$1", "", "$2", "")
  5867.  
  5868. CreateSettingsUI()
  5869. {
  5870.     Global
  5871.     ; General
  5872.  
  5873.     GuiAddGroupBox("General", "x7 y15 w260 h90")
  5874.    
  5875.     ; Note: window handles (hwnd) are only needed if a UI tooltip should be attached.
  5876.    
  5877.     GuiAddCheckbox("Only show tooltip if PoE is frontmost", "x17 y35 w210 h30", Opts.OnlyActiveIfPOEIsFront, "OnlyActiveIfPOEIsFront", "OnlyActiveIfPOEIsFrontH")
  5878.     AddToolTip(OnlyActiveIfPOEIsFrontH, "If checked the script does nothing if the`nPath of Exile window isn't the frontmost")
  5879.     GuiAddCheckbox("Put tooltip results on clipboard", "x17 y65 w210 h30", Opts.PutResultsOnClipboard, "PutResultsOnClipboard", "PutResultsOnClipboardH")
  5880.     AddToolTip(PutResultsOnClipboardH, "Put tooltip result text onto the system clipboard`n(overwriting the item info text PoE put there to begin with)")
  5881.    
  5882.     ; Display - All Gear
  5883.  
  5884.     GuiAddGroupBox("Display - All Gear", "x7 y115 w260 h90")
  5885.    
  5886.     GuiAddCheckbox("Show item level", "x17 y135 w210 h30", Opts.ShowItemLevel, "ShowItemLevel")
  5887.     GuiAddCheckbox("Show max sockets based on item lvl", "x17 y165 w210 h30", Opts.ShowMaxSockets, "ShowMaxSockets", "ShowMaxSocketsH")
  5888.     AddToolTip(ShowMaxSocketsH, "Show maximum amount of sockets the item can have`nbased on its item level")
  5889.  
  5890.     ; Display - Weapons
  5891.  
  5892.     GuiAddGroupBox("Display - Weapons", "x7 y215 w260 h60")
  5893.  
  5894.     GuiAddCheckbox("Show damage calculations", "x17 y235 w210 h30", Opts.ShowDamageCalculations, "ShowDamageCalculations")
  5895.  
  5896.     ; Display - Other
  5897.  
  5898.     GuiAddGroupBox("Display - Other", "x7 y285 w260 h60")
  5899.  
  5900.     GuiAddCheckbox("Show currency value in chaos", "x17 y305 w210 h30", Opts.ShowCurrencyValueInChaos, "ShowCurrencyValueInChaos")
  5901.  
  5902.     ; Valuable Evaluations
  5903.  
  5904.     GuiAddGroupBox("Valuable Evaluations", "x7 y355 w260 h150")
  5905.  
  5906.     GuiAddCheckbox("Show unique evaluation", "x17 y375 w210 h30", Opts.ShowUniqueEvaluation, "ShowUniqueEvaluation", "ShowUniqueEvaluationH")
  5907.     AddToolTip(ShowUniqueEvaluationH, "Mark unique as valuable based on its item name`n(can be edited in data\ValuableUniques.txt)")
  5908.     GuiAddCheckbox("Show gem evaluation", "x17 y405 w210 h30", Opts.ShowGemEvaluation, "ShowGemEvaluation", "ShowGemEvaluationH", "SettingsUI_ChkShowGemEvaluation")
  5909.     AddToolTip(ShowGemEvaluationH, "Mark gem as valuable if quality is higher`nthan the following threshold`n(can be edited in data\ValuableGems.txt)")
  5910.         GuiAddText("Gem quality valuable threshold:", "x37 y439 w150 h20", "LblGemQualityThreshold")
  5911.         GuiAddEdit(Opts.GemQualityValueThreshold, "x197 y437 w40 h20", "GemQualityValueThreshold")
  5912.     GuiAddCheckbox("Mark high number of links as valuable", "x17 y465 w210 h30", Opts.MarkHighLinksAsValuable, "MarkHighLinksAsValuable")
  5913.    
  5914.     ; Display - Affixes
  5915.  
  5916.     GuiAddGroupBox("Display - Affixes", "x277 y15 w260 h330")
  5917.  
  5918.     GuiAddCheckbox("Show affix totals", "x287 y35 w210 h30", Opts.ShowAffixTotals, "ShowAffixTotals", "ShowAffixTotalsH")
  5919.     AddToolTip(ShowAffixTotalsH, "Show a statistic how many prefixes and suffixes`nthe item has")
  5920.     GuiAddCheckbox("Show affix details", "x287 y65 w210 h30", Opts.ShowAffixDetails, "ShowAffixDetails", "ShowAffixDetailsH", "SettingsUI_ChkShowAffixDetails")
  5921.     AddToolTip(ShowAffixDetailsH, "Show detailed affix breakdown. Note that crafted mods are not`nsupported and some ranges are guesstimated (marked with a *)")
  5922.         GuiAddCheckbox("Mirror affix lines", "x307 y95 w190 h30", Opts.MirrorAffixLines, "MirrorAffixLines", "MirrorAffixLinesH")
  5923.         AddToolTip(MirrorAffixLinesH, "Display truncated affix names within the breakdown")
  5924.     GuiAddCheckbox("Show affix level", "x287 y125 w210 h30", Opts.ShowAffixLevel, "ShowAffixLevel", "ShowAffixLevelH")
  5925.         AddToolTip(ShowAffixLevelH, "Show item level of the displayed affix value bracket")
  5926.     GuiAddCheckbox("Show affix bracket", "x287 y155 w210 h30", Opts.ShowAffixBracket, "ShowAffixBracket", "ShowAffixBracketH")
  5927.         AddToolTip(ShowAffixBracketH, "Show affix value bracket as is on the item")
  5928.     GuiAddCheckbox("Show affix max possible", "x287 y185 w210 h30", Opts.ShowAffixMaxPossible, "ShowAffixMaxPossible", "ShowAffixMaxPossibleH", "SettingsUI_ChkShowAffixMaxPossible")
  5929.         AddToolTip(ShowAffixMaxPossibleH, "Show max possible affix value bracket")
  5930.         GuiAddCheckbox("Max span starting from first", "x307 y215 w190 h30", Opts.MaxSpanStartingFromFirst, "MaxSpanStartingFromFirst", "MaxSpanStartingFromFirstH")
  5931.         AddToolTip(MaxSpanStartingFromFirstH, "Construct a pseudo range by combining the lowest possible`naffix value bracket with the max possible based on item level")
  5932.     GuiAddCheckbox("Show affix bracket tier", "x287 y245 w210 h30", Opts.ShowAffixBracketTier, "ShowAffixBracketTier", "ShowAffixBracketTierH", "SettingsUI_ChkShowAffixBracketTier")
  5933.         AddToolTip(ShowAffixBracketTierH, "Display affix bracket tier in reverse ordering,`nT1 being the best possible roll.")
  5934.         GuiAddCheckbox("Tier relative to item lvl", "x307 y275 w190 h20", Opts.TierRelativeToItemLevel, "TierRelativeToItemLevel", "TierRelativeToItemLevelH")
  5935.         GuiAddText("(hold Shift to toggle temporarily)", "x330 y295 w190 h20", "LblTierRelativeToItemLevelOverrideNote")
  5936.         AddToolTip(TierRelativeToItemLevelH, "When showing affix bracket tier, make T1 being best possible`ntaking item level into account.")
  5937.         GuiAddCheckbox("Show affix bracket tier total", "x307 y315 w190 h20", Opts.ShowAffixBracketTierTotal, "ShowAffixBracketTierTotal", "ShowAffixBracketTierTotalH")
  5938.         AddToolTip(ShowAffixBracketTierTotalH, "Show number of total affix bracket tiers in format T/N,`n where T = tier on item, N = number of total tiers available")
  5939.        
  5940.     ; Display - Results
  5941.  
  5942.     GuiAddGroupBox("Display - Results", "x277 y355 w260 h210")
  5943.    
  5944.     GuiAddCheckbox("Compact double ranges", "x287 y375 w210 h30", Opts.CompactDoubleRanges, "CompactDoubleRanges", "CompactDoubleRangesH")
  5945.     AddToolTip(CompactDoubleRangesH, "Show double ranges as one range,`ne.g. x-y (to) z-w becomes x-w")
  5946.     GuiAddCheckbox("Compact affix types", "x287 y405 w210 h30", Opts.CompactAffixTypes, "CompactAffixTypes", "CompactAffixTypesH")
  5947.     AddToolTip(CompactAffixTypesH, "Replace affix type with a short-hand version,`ne.g. P=Prefix, S=Suffix, CP=Composite")
  5948.  
  5949.     GuiAddText("Mirror line field width:", "x287 y447 w110 h20", "LblMirrorLineFieldWidth")
  5950.     GuiAddEdit(Opts.MirrorLineFieldWidth, "x407 y445 w40 h20", "MirrorLineFieldWidth")
  5951.     GuiAddText("Value range field width:", "x287 y487 w120 h20", "LblValueRangeFieldWidth")
  5952.     GuiAddEdit(Opts.ValueRangeFieldWidth, "x407 y485 w40 h20", "ValueRangeFieldWidth")
  5953.     GuiAddText("Affix detail delimiter:", "x287 y507 w120 h20", "LblAffixDetailDelimiter")
  5954.     GuiAddEdit(Opts.AffixDetailDelimiter, "x407 y505 w40 h20", "AffixDetailDelimiter")
  5955.     GuiAddText("Affix detail ellipsis:", "x287 y537 w120 h20", "LblAffixDetailEllipsis")
  5956.     GuiAddEdit(Opts.AffixDetailEllipsis, "x407 y535 w40 h20", "AffixDetailEllipsis")
  5957.    
  5958.     ; Tooltip
  5959.  
  5960.     GuiAddGroupBox("Tooltip", "x7 y515 w260 h185")
  5961.    
  5962.     GuiAddCheckBox("Use tooltip timeout", "x17 y530 w210 h30", Opts.UseTooltipTimeout, "UseTooltipTimeout", "UseTooltipTimeoutH", "SettingsUI_ChkUseTooltipTimeout")
  5963.     AddToolTip(UseTooltipTimeoutH, "Hide tooltip automatically after x amount of ticks have passed")
  5964.         GuiAddText("Timeout ticks (1 tick = 100ms):", "x27 y562 w150 h20", "LblToolTipTimeoutTicks")
  5965.         GuiAddEdit(Opts.ToolTipTimeoutTicks, "x187 y560 w50 h20", "ToolTipTimeoutTicks")
  5966.  
  5967.     GuiAddCheckbox("Display at fixed coordinates", "x17 y580 w230 h30", Opts.DisplayToolTipAtFixedCoords, "DisplayToolTipAtFixedCoords", "DisplayToolTipAtFixedCoordsH", "SettingsUI_ChkDisplayToolTipAtFixedCoords")
  5968.     AddToolTip(DisplayToolTipAtFixedCoordsH, "Show tooltip in virtual screen space at the fixed`ncoordinates given below. Virtual screen space means`nthe full desktop frame, including any secondary`nmonitors. Coords are relative to the top left edge`nand increase going down and to the right.")
  5969.         GuiAddText("X:", "x37 y612 w20 h20", "LblScreenOffsetX")
  5970.         GuiAddEdit(Opts.ScreenOffsetX, "x55 y610 w40 h20", "ScreenOffsetX")
  5971.         GuiAddText("Y:", "x105 y612 w20 h20", "LblScreenOffsetY")
  5972.         GuiAddEdit(Opts.ScreenOffsetY, "x125 y610 w40 h20", "ScreenOffsetY")
  5973.  
  5974.     GuiAddText("Mousemove threshold (px):", "x17 y642 w160 h20", "LblMouseMoveThreshold", "LblMouseMoveThresholdH")
  5975.     AddToolTip(LblMouseMoveThresholdH, "Hide tooltip automatically after the mouse has moved x amount of pixels")
  5976.     GuiAddEdit(Opts.MouseMoveThreshold, "x187 y640 w50 h20", "MouseMoveThreshold", "MouseMoveThresholdH")
  5977.  
  5978.     GuiAddText("Font Size:", "x17 y672 w160 h20", "LblFontSize")
  5979.     GuiAddEdit(Opts.FontSize, "x187 y670 w50 h20", "FontSize")
  5980.  
  5981.     GuiAddText("Mouse over settings or see the beginning of the PoE-Item-Info.ahk script for comments on what these settings do exactly.", "x277 y575 w250 h60")
  5982.  
  5983.     GuiAddButton("&Defaults", "x287 y670 w80 h23", "SettingsUI_BtnDefaults")
  5984.     GuiAddButton("&OK", "Default x372 y670 w75 h23", "SettingsUI_BtnOK")
  5985.     GuiAddButton("&Cancel", "x452 y670 w80 h23", "SettingsUI_BtnCancel")
  5986. }
  5987.  
  5988. UpdateSettingsUI()
  5989. {    
  5990.     Global
  5991.  
  5992.     GuiControl,, OnlyActiveIfPOEIsFront, % Opts.OnlyActiveIfPOEIsFront
  5993.     GuiControl,, PutResultsOnClipboard, % Opts.PutResultsOnClipboard
  5994.     GuiControl,, ShowItemLevel, % Opts.ShowItemLevel
  5995.     GuiControl,, ShowMaxSockets, % Opts.ShowMaxSockets
  5996.     GuiControl,, ShowDamageCalculations, % Opts.ShowDamageCalculations
  5997.     GuiControl,, ShowCurrencyValueInChaos, % Opts.ShowCurrencyValueInChaos
  5998.     GuiControl,, DisplayToolTipAtFixedCoords, % Opts.DisplayToolTipAtFixedCoords
  5999.     If (Opts.DisplayToolTipAtFixedCoords == False)
  6000.     {
  6001.         GuiControl, Disable, LblScreenOffsetX
  6002.         GuiControl, Disable, ScreenOffsetX
  6003.         GuiControl, Disable, LblScreenOffsetY
  6004.         GuiControl, Disable, ScreenOffsetY        
  6005.     }
  6006.     Else
  6007.     {
  6008.         GuiControl, Enable, LblScreenOffsetX
  6009.         GuiControl, Enable, ScreenOffsetX
  6010.         GuiControl, Enable, LblScreenOffsetY
  6011.         GuiControl, Enable, ScreenOffsetY
  6012.     }
  6013.     ;~ GetScreenInfo()
  6014.     ;~ If (Globals.Get("MonitorCount", 1) > 1)
  6015.     ;~ {
  6016.         ;~ GuiControl,, DisplayToolTipAtFixedCoords, % Opts.DisplayToolTipAtFixedCoords
  6017.         ;~ GuiControl,, ScreenOffsetX, % Opts.ScreenOffsetX
  6018.         ;~ GuiControl,, ScreenOffsetY, % Opts.ScreenOffsetY
  6019.         ;~ GuiControl, Enable, DisplayToolTipAtFixedCoords
  6020.         ;~ GuiControl, Enable, LblScreenOffsetX
  6021.         ;~ GuiControl, Enable, ScreenOffsetX
  6022.         ;~ GuiControl, Enable, LblScreenOffsetY
  6023.         ;~ GuiControl, Enable, ScreenOffsetY
  6024.     ;~ }
  6025.     ;~ Else
  6026.     ;~ {
  6027.         ;~ GuiControl,, DisplayToolTipAtFixedCoords, 0
  6028.         ;~ GuiControl,, ScreenOffsetX, 0
  6029.         ;~ GuiControl,, ScreenOffsetY, 0
  6030.         ;~ GuiControl, Disable, DisplayToolTipAtFixedCoords
  6031.         ;~ GuiControl, Disable, LblScreenOffsetX
  6032.         ;~ GuiControl, Disable, ScreenOffsetX
  6033.         ;~ GuiControl, Disable, LblScreenOffsetY
  6034.         ;~ GuiControl, Disable, ScreenOffsetY
  6035.     ;~ }
  6036.    
  6037.     GuiControl,, ShowUniqueEvaluation, % Opts.ShowUniqueEvaluation
  6038.     GuiControl,, ShowGemEvaluation, % Opts.ShowGemEvaluation
  6039.     If (Opts.ShowGemEvaluation == False)
  6040.     {
  6041.         GuiControl, Disable, LblGemQualityThreshold
  6042.         GuiControl, Disable, GemQualityValueThreshold
  6043.     }
  6044.     Else
  6045.     {
  6046.         GuiControl, Enable, LblGemQualityThreshold
  6047.         GuiControl, Enable, GemQualityValueThreshold
  6048.     }
  6049.     GuiControl,, GemQualityValueThreshold, % Opts.GemQualityValueThreshold
  6050.     GuiControl,, MarkHighLinksAsValuable, % Opts.MarkHighLinksAsValuable
  6051.    
  6052.     GuiControl,, ShowAffixTotals, % Opts.ShowAffixTotals
  6053.     GuiControl,, ShowAffixDetails, % Opts.ShowAffixDetails
  6054.     If (Opts.ShowAffixDetails == False)
  6055.     {
  6056.         GuiControl, Disable, MirrorAffixLines
  6057.     }
  6058.     Else
  6059.     {
  6060.         GuiControl, Enable, MirrorAffixLines
  6061.     }
  6062.     GuiControl,, MirrorAffixLines, % Opts.MirrorAffixLines
  6063.     GuiControl,, ShowAffixLevel, % Opts.ShowAffixLevel
  6064.     GuiControl,, ShowAffixBracket, % Opts.ShowAffixBracket
  6065.     GuiControl,, ShowAffixMaxPossible, % Opts.ShowAffixMaxPossible
  6066.     If (Opts.ShowAffixMaxPossible == False)
  6067.     {
  6068.         GuiControl, Disable, MaxSpanStartingFromFirst
  6069.     }
  6070.     Else
  6071.     {
  6072.         GuiControl, Enable, MaxSpanStartingFromFirst
  6073.     }
  6074.     GuiControl,, MaxSpanStartingFromFirst, % Opts.MaxSpanStartingFromFirst
  6075.     GuiControl,, ShowAffixBracketTier, % Opts.ShowAffixBracketTier
  6076.     GuiControl,, ShowAffixBracketTierTotal, % Opts.ShowAffixBracketTierTotal
  6077.     If (Opts.ShowAffixBracketTier == False)
  6078.     {
  6079.         GuiControl, Disable, TierRelativeToItemLevel
  6080.         GuiControl, Disable, ShowAffixBracketTierTotal
  6081.     }
  6082.     Else
  6083.     {
  6084.         GuiControl, Enable, TierRelativeToItemLevel
  6085.         GuiControl, Enable, ShowAffixBracketTierTotal
  6086.     }
  6087.     GuiControl,, TierRelativeToItemLevel, % Opts.TierRelativeToItemLevel
  6088.     GuiControl,, CompactDoubleRanges, % Opts.CompactDoubleRanges
  6089.     GuiControl,, CompactAffixTypes, % Opts.CompactAffixTypes
  6090.     GuiControl,, MirrorLineFieldWidth, % Opts.MirrorLineFieldWidth
  6091.     GuiControl,, ValueRangeFieldWidth, % Opts.ValueRangeFieldWidth
  6092.     GuiControl,, AffixDetailDelimiter, % Opts.AffixDetailDelimiter
  6093.     GuiControl,, AffixDetailEllipsis, % Opts.AffixDetailEllipsis
  6094.    
  6095.     GuiControl,, UseTooltipTimeout, % Opts.UseTooltipTimeout
  6096.     If (Opts.UseTooltipTimeout == False)
  6097.     {
  6098.         GuiControl, Disable, LblToolTipTimeoutTicks
  6099.         GuiControl, Disable, ToolTipTimeoutTicks
  6100.     }
  6101.     Else
  6102.     {
  6103.         GuiControl, Enable, LblToolTipTimeoutTicks
  6104.         GuiControl, Enable, ToolTipTimeoutTicks
  6105.     }
  6106.     GuiControl,, ToolTipTimeoutTicks, % Opts.ToolTipTimeoutTicks
  6107.     GuiControl,, MouseMoveThreshold, % Opts.MouseMoveThreshold
  6108.     GuiControl,, FontSize, % Opts.FontSize
  6109. }
  6110.  
  6111. ShowSettingsUI()
  6112. {
  6113.     ; remove POE-Item-Info tooltip if still visible
  6114.     SetTimer, ToolTipTimer, Off
  6115.     ToolTip
  6116.     Fonts.SetUIFont(9)
  6117.     Gui, Show, w545 h710, PoE Item Info Settings
  6118. }
  6119.  
  6120. IniRead(ConfigPath, Section_, Key, Default_)
  6121. {
  6122.     Result := ""
  6123.     IniRead, Result, %ConfigPath%, %Section_%, %Key%, %Default_%
  6124.     return Result
  6125. }
  6126.  
  6127. IniWrite(Val, ConfigPath, Section_, Key)
  6128. {
  6129.     IniWrite, %Val%, %ConfigPath%, %Section_%, %Key%
  6130. }
  6131.  
  6132. ReadConfig(ConfigPath="config.ini")
  6133. {
  6134.     Global
  6135.     IfExist, %ConfigPath%
  6136.     {
  6137.         ; General
  6138.        
  6139.         Opts.OnlyActiveIfPOEIsFront := IniRead(ConfigPath, "General", "OnlyActiveIfPOEIsFront", Opts.OnlyActiveIfPOEIsFront)
  6140.         Opts.PutResultsOnClipboard := IniRead(ConfigPath, "General", "PutResultsOnClipboard", Opts.PutResultsOnClipboard)
  6141.        
  6142.         ; Display - All Gear
  6143.        
  6144.         Opts.ShowItemLevel := IniRead(ConfigPath, "DisplayAllGear", "ShowItemLevel", Opts.ShowItemLevel)
  6145.         Opts.ShowMaxSockets := IniRead(ConfigPath, "DisplayAllGear", "ShowMaxSockets", Opts.ShowMaxSockets)
  6146.        
  6147.         ; Display - Weapons
  6148.        
  6149.         Opts.ShowDamageCalculations := IniRead(ConfigPath, "DisplayWeapons", "ShowDamageCalculations", Opts.ShowDamageCalculations)
  6150.        
  6151.         ; Display - Other
  6152.        
  6153.         Opts.ShowCurrencyValueInChaos := IniRead(ConfigPath, "DisplayOther", "ShowCurrencyValueInChaos", Opts.ShowCurrencyValueInChaos)
  6154.        
  6155.         ; Valuable Evaluations
  6156.        
  6157.         Opts.ShowUniqueEvaluation := IniRead(ConfigPath, "ValuableEvaluations", "ShowUniqueEvaluation", Opts.ShowUniqueEvaluation)
  6158.         Opts.ShowGemEvaluation := IniRead(ConfigPath, "ValuableEvaluations", "ShowGemEvaluation", Opts.ShowGemEvaluation)
  6159.         Opts.GemQualityValueThreshold := IniRead(ConfigPath, "ValuableEvaluations", "GemQualityValueThreshold", Opts.GemQualityValueThreshold)
  6160.         Opts.MarkHighLinksAsValuable := IniRead(ConfigPath, "ValuableEvaluations", "MarkHighLinksAsValuable", Opts.MarkHighLinksAsValuable)
  6161.        
  6162.         ; Display - Affixes
  6163.        
  6164.         Opts.ShowAffixTotals := IniRead(ConfigPath, "DisplayAffixes", "ShowAffixTotals", Opts.ShowAffixTotals)
  6165.         Opts.ShowAffixDetails := IniRead(ConfigPath, "DisplayAffixes", "ShowAffixDetails", Opts.ShowAffixDetails)
  6166.         Opts.MirrorAffixLines := IniRead(ConfigPath, "DisplayAffixes", "MirrorAffixLines", Opts.MirrorAffixLines)
  6167.         Opts.ShowAffixLevel := IniRead(ConfigPath, "DisplayAffixes", "ShowAffixLevel", Opts.ShowAffixLevel)
  6168.         Opts.ShowAffixBracket := IniRead(ConfigPath, "DisplayAffixes", "ShowAffixBracket", Opts.ShowAffixBracket)
  6169.         Opts.ShowAffixMaxPossible := IniRead(ConfigPath, "DisplayAffixes", "ShowAffixMaxPossible", Opts.ShowAffixMaxPossible)
  6170.         Opts.MaxSpanStartingFromFirst := IniRead(ConfigPath, "DisplayAffixes", "MaxSpanStartingFromFirst", Opts.MaxSpanStartingFromFirst)
  6171.         Opts.ShowAffixBracketTier := IniRead(ConfigPath, "DisplayAffixes", "ShowAffixBracketTier", Opts.ShowAffixBracketTier)
  6172.         Opts.TierRelativeToItemLevel := IniRead(ConfigPath, "DisplayAffixes", "TierRelativeToItemLevel", Opts.TierRelativeToItemLevel)
  6173.         Opts.ShowAffixBracketTierTotal := IniRead(ConfigPath, "DisplayAffixes", "ShowAffixBracketTierTotal", Opts.ShowAffixBracketTierTotal)
  6174.        
  6175.         ; Display - Results
  6176.        
  6177.         Opts.CompactDoubleRanges := IniRead(ConfigPath, "DisplayResults", "CompactDoubleRanges", Opts.CompactDoubleRanges)
  6178.         Opts.CompactAffixTypes := IniRead(ConfigPath, "DisplayResults", "CompactAffixTypes", Opts.CompactAffixTypes)
  6179.         Opts.MirrorLineFieldWidth := IniRead(ConfigPath, "DisplayResults", "MirrorLineFieldWidth", Opts.MirrorLineFieldWidth)
  6180.         Opts.ValueRangeFieldWidth := IniRead(ConfigPath, "DisplayResults", "ValueRangeFieldWidth", Opts.ValueRangeFieldWidth)
  6181.         Opts.AffixDetailDelimiter := IniRead(ConfigPath, "DisplayResults", "AffixDetailDelimiter", Opts.AffixDetailDelimiter)
  6182.         Opts.AffixDetailEllipsis := IniRead(ConfigPath, "DisplayResults", "AffixDetailEllipsis", Opts.AffixDetailEllipsis)
  6183.        
  6184.         ; Tooltip
  6185.        
  6186.         Opts.MouseMoveThreshold := IniRead(ConfigPath, "Tooltip", "MouseMoveThreshold", Opts.MouseMoveThreshold)
  6187.         Opts.UseTooltipTimeout := IniRead(ConfigPath, "Tooltip", "UseTooltipTimeout", Opts.UseTooltipTimeout)
  6188.         Opts.DisplayToolTipAtFixedCoords := IniRead(ConfigPath, "Tooltip", "DisplayToolTipAtFixedCoords", Opts.DisplayToolTipAtFixedCoords)
  6189.         Opts.ScreenOffsetX := IniRead(ConfigPath, "Tooltip", "ScreenOffsetX", Opts.ScreenOffsetX)
  6190.         Opts.ScreenOffsetY := IniRead(ConfigPath, "Tooltip", "ScreenOffsetY", Opts.ScreenOffsetY)
  6191.         Opts.ToolTipTimeoutTicks := IniRead(ConfigPath, "Tooltip", "ToolTipTimeoutTicks", Opts.ToolTipTimeoutTicks)
  6192.         Opts.FontSize := IniRead(ConfigPath, "Tooltip", "FontSize", Opts.FontSize)
  6193.     }
  6194. }
  6195.  
  6196. WriteConfig(ConfigPath="config.ini")
  6197. {
  6198.     Global
  6199.     Opts.ScanUI()
  6200.    
  6201.     ; General
  6202.    
  6203.     IniWrite(Opts.OnlyActiveIfPOEIsFront, ConfigPath, "General", "OnlyActiveIfPOEIsFront")
  6204.     IniWrite(Opts.PutResultsOnClipboard, ConfigPath, "General", "PutResultsOnClipboard")
  6205.    
  6206.     ; Display - All Gear
  6207.    
  6208.     IniWrite(Opts.ShowItemLevel, ConfigPath, "DisplayAllGear", "ShowItemLevel")
  6209.     IniWrite(Opts.ShowMaxSockets, ConfigPath, "DisplayAllGear", "ShowMaxSockets")
  6210.    
  6211.     ; Display - Weapons
  6212.    
  6213.     IniWrite(Opts.ShowDamageCalculations, ConfigPath, "DisplayWeapons", "ShowDamageCalculations")
  6214.    
  6215.     ; Display - Other
  6216.    
  6217.     IniWrite(Opts.ShowCurrencyValueInChaos, ConfigPath, "DisplayOther", "ShowCurrencyValueInChaos")
  6218.    
  6219.     ; Valuable Evaluations
  6220.    
  6221.     IniWrite(Opts.ShowUniqueEvaluation, ConfigPath, "ValuableEvaluations", "ShowUniqueEvaluation")
  6222.     IniWrite(Opts.ShowGemEvaluation, ConfigPath, "ValuableEvaluations", "ShowGemEvaluation")
  6223.     IniWrite(Opts.GemQualityValueThreshold, ConfigPath, "ValuableEvaluations", "GemQualityValueThreshold")
  6224.     IniWrite(Opts.MarkHighLinksAsValuable, ConfigPath, "ValuableEvaluations", "MarkHighLinksAsValuable")
  6225.    
  6226.     ; Display - Affixes
  6227.    
  6228.     IniWrite(Opts.ShowAffixTotals, ConfigPath, "DisplayAffixes", "ShowAffixTotals")
  6229.     IniWrite(Opts.ShowAffixDetails, ConfigPath, "DisplayAffixes", "ShowAffixDetails")
  6230.     IniWrite(Opts.MirrorAffixLines, ConfigPath, "DisplayAffixes", "MirrorAffixLines")
  6231.     IniWrite(Opts.ShowAffixLevel, ConfigPath, "DisplayAffixes", "ShowAffixLevel")
  6232.     IniWrite(Opts.ShowAffixBracket, ConfigPath, "DisplayAffixes", "ShowAffixBracket")
  6233.     IniWrite(Opts.ShowAffixMaxPossible, ConfigPath, "DisplayAffixes", "ShowAffixMaxPossible")
  6234.     IniWrite(Opts.MaxSpanStartingFromFirst, ConfigPath, "DisplayAffixes", "MaxSpanStartingFromFirst")
  6235.     IniWrite(Opts.ShowAffixBracketTier, ConfigPath, "DisplayAffixes", "ShowAffixBracketTier")
  6236.     IniWrite(Opts.TierRelativeToItemLevel, ConfigPath, "DisplayAffixes", "TierRelativeToItemLevel")
  6237.     IniWrite(Opts.ShowAffixBracketTierTotal, ConfigPath, "DisplayAffixes", "ShowAffixBracketTierTotal")
  6238.    
  6239.     ; Display - Results
  6240.    
  6241.     IniWrite(Opts.CompactDoubleRanges, ConfigPath, "DisplayResults", "CompactDoubleRanges")
  6242.     IniWrite(Opts.CompactAffixTypes, ConfigPath, "DisplayResults", "CompactAffixTypes")
  6243.     IniWrite(Opts.MirrorLineFieldWidth, ConfigPath, "DisplayResults", "MirrorLineFieldWidth")
  6244.     IniWrite(Opts.ValueRangeFieldWidth, ConfigPath, "DisplayResults", "ValueRangeFieldWidth")
  6245.     If IsEmptyString(Opts.AffixDetailDelimiter)
  6246.     {
  6247.         IniWrite("""" . Opts.AffixDetailDelimiter . """", ConfigPath, "DisplayResults", "AffixDetailDelimiter")
  6248.     }
  6249.     Else
  6250.     {
  6251.         IniWrite(Opts.AffixDetailDelimiter, ConfigPath, "DisplayResults", "AffixDetailDelimiter")
  6252.     }
  6253.     IniWrite(Opts.AffixDetailEllipsis, ConfigPath, "DisplayResults", "AffixDetailEllipsis")
  6254.    
  6255.     ; Tooltip
  6256.    
  6257.     IniWrite(Opts.MouseMoveThreshold, ConfigPath, "Tooltip", "MouseMoveThreshold")
  6258.     IniWrite(Opts.UseTooltipTimeout, ConfigPath, "Tooltip", "UseTooltipTimeout")
  6259.     IniWrite(Opts.DisplayToolTipAtFixedCoords, ConfigPath, "Tooltip", "DisplayToolTipAtFixedCoords")
  6260.     IniWrite(Opts.ScreenOffsetX, ConfigPath, "Tooltip", "ScreenOffsetX")
  6261.     IniWrite(Opts.ScreenOffsetY, ConfigPath, "Tooltip", "ScreenOffsetY")
  6262.     IniWrite(Opts.ToolTipTimeoutTicks, ConfigPath, "Tooltip", "ToolTipTimeoutTicks")
  6263.     IniWrite(Opts.FontSize, ConfigPath, "Tooltip", "FontSize")
  6264. }
  6265.  
  6266. CopyDefaultConfig()
  6267. {
  6268.     FileCopy, %A_ScriptDir%\data\defaults.ini, %A_ScriptDir%
  6269.     FileMove, %A_ScriptDir%\defaults.ini, %A_ScriptDir%\config.ini
  6270. }
  6271.  
  6272. RemoveConfig()
  6273. {
  6274.     FileDelete, %A_ScriptDir%\config.ini
  6275. }
  6276.  
  6277. CreateDefaultConfig()
  6278. {
  6279.     WriteConfig(A_ScriptDir . "\data\defaults.ini")
  6280. }
  6281.  
  6282. GetContributors(AuthorsPerLine=0)
  6283. {
  6284.     IfNotExist, %A_ScriptDir%\AUTHORS.txt
  6285.     {
  6286.         return "`r`n AUTHORS.txt missing `r`n"
  6287.     }
  6288.     Authors := "`r`n"
  6289.     i := 0
  6290.     Loop, Read, %A_ScriptDir%\AUTHORS.txt, `r, `n
  6291.     {
  6292.         Authors := Authors . A_LoopReadLine . " "
  6293.         i += 1
  6294.         if (AuthorsPerLine != 0 and mod(i, AuthorsPerLine) == 0) ; every four authors
  6295.         {
  6296.             Authors := Authors . "`r`n"
  6297.         }
  6298.     }
  6299.     return Authors
  6300. }
  6301.  
  6302. ; ########### TIMERS ############
  6303.  
  6304. ; Tick every 100 ms
  6305. ; Remove tooltip if mouse is moved or 5 seconds pass
  6306. ToolTipTimer:
  6307.     Global Opts, ToolTipTimeout
  6308.     ToolTipTimeout += 1
  6309.     MouseGetPos, CurrX, CurrY
  6310.     MouseMoved := (CurrX - X) ** 2 + (CurrY - Y) ** 2 > Opts.MouseMoveThreshold ** 2
  6311.     If (MouseMoved or ((UseTooltipTimeout == 1) and (ToolTipTimeout >= Opts.ToolTipTimeoutTicks)))
  6312.     {
  6313.         SetTimer, ToolTipTimer, Off
  6314.         ToolTip
  6315.     }
  6316.     return
  6317.  
  6318. OnClipBoardChange:
  6319.     Global Opts
  6320.     If (Opts.OnlyActiveIfPOEIsFront)
  6321.     {
  6322.         ; do nothing if Path of Exile isn't the foremost window
  6323.         IfWinActive, Path of Exile ahk_class Direct3DWindowClass
  6324.         {
  6325.             ParseClipBoardChanges()
  6326.         }
  6327.     }
  6328.     Else
  6329.     {
  6330.         ; if running tests parse clipboard regardless if PoE is foremost
  6331.         ; so we can check individual cases from test case text files
  6332.         ParseClipBoardChanges()
  6333.     }
  6334.     return
  6335.  
  6336. ShowSettingsUI:
  6337.     ReadConfig()
  6338.     Sleep, 50
  6339.     UpdateSettingsUI()
  6340.     Sleep, 50
  6341.     ShowSettingsUI()
  6342.     return
  6343.    
  6344. SettingsUI_BtnOK:
  6345.     Global Opts
  6346.     Gui, Submit
  6347.     Sleep, 50
  6348.     WriteConfig()
  6349.     UpdateSettingsUI()
  6350.     Fonts.SetFixedFont(GuiGet("FontSize", Opts.FontSize))
  6351.     return
  6352.  
  6353. SettingsUI_BtnCancel:
  6354.     Gui, Cancel
  6355.     return
  6356.  
  6357. SettingsUI_BtnDefaults:
  6358.     Gui, Cancel
  6359.     RemoveConfig()
  6360.     Sleep, 75
  6361.     CopyDefaultConfig()
  6362.     Sleep, 75
  6363.     ReadConfig()
  6364.     Sleep, 75
  6365.     UpdateSettingsUI()
  6366.     ShowSettingsUI()
  6367.     return
  6368.    
  6369. SettingsUI_ChkShowGemEvaluation:
  6370.     GuiControlGet, IsChecked,, ShowGemEvaluation
  6371.     If (Not IsChecked)
  6372.     {
  6373.         GuiControl, Disable, LblGemQualityThreshold
  6374.         GuiControl, Disable, GemQualityValueThreshold
  6375.     }
  6376.     Else
  6377.     {
  6378.         GuiControl, Enable, LblGemQualityThreshold
  6379.         GuiControl, Enable, GemQualityValueThreshold
  6380.     }
  6381.     return
  6382.    
  6383. SettingsUI_ChkShowAffixDetails:
  6384.     GuiControlGet, IsChecked,, ShowAffixDetails
  6385.     If (Not IsChecked)
  6386.     {
  6387.         GuiControl, Disable, MirrorAffixLines
  6388.     }
  6389.     Else
  6390.     {
  6391.         GuiControl, Enable, MirrorAffixLines
  6392.     }
  6393.     return
  6394.  
  6395. SettingsUI_ChkShowAffixMaxPossible:
  6396.     GuiControlGet, IsChecked,, ShowAffixMaxPossible
  6397.     If (Not IsChecked)
  6398.     {
  6399.         GuiControl, Disable, MaxSpanStartingFromFirst
  6400.     }
  6401.     Else
  6402.     {
  6403.         GuiControl, Enable, MaxSpanStartingFromFirst
  6404.     }
  6405.     return
  6406.    
  6407. SettingsUI_ChkShowAffixBracketTier:
  6408.     GuiControlGet, IsChecked,, ShowAffixBracketTier
  6409.     If (Not IsChecked)
  6410.     {
  6411.         GuiControl, Disable, TierRelativeToItemLevel
  6412.         GuiControl, Disable, ShowAffixBracketTierTotal
  6413.     }
  6414.     Else
  6415.     {
  6416.         GuiControl, Enable, TierRelativeToItemLevel
  6417.         GuiControl, Enable, ShowAffixBracketTierTotal
  6418.     }
  6419.     return
  6420.    
  6421. SettingsUI_ChkUseTooltipTimeout:
  6422.     GuiControlGet, IsChecked,, UseTooltipTimeout
  6423.     If (Not IsChecked)
  6424.     {
  6425.         GuiControl, Disable, LblToolTipTimeoutTicks
  6426.         GuiControl, Disable, ToolTipTimeoutTicks
  6427.     }
  6428.     Else
  6429.     {
  6430.         GuiControl, Enable, LblToolTipTimeoutTicks
  6431.         GuiControl, Enable, ToolTipTimeoutTicks
  6432.     }
  6433.     return
  6434.    
  6435. SettingsUI_ChkDisplayToolTipAtFixedCoords:
  6436.     GuiControlGet, IsChecked,, DisplayToolTipAtFixedCoords
  6437.     If (Not IsChecked)
  6438.     {
  6439.         GuiControl, Disable, LblScreenOffsetX
  6440.         GuiControl, Disable, ScreenOffsetX
  6441.         GuiControl, Disable, LblScreenOffsetY
  6442.         GuiControl, Disable, ScreenOffsetY
  6443.     }
  6444.     Else
  6445.     {
  6446.         GuiControl, Enable, LblScreenOffsetX
  6447.         GuiControl, Enable, ScreenOffsetX
  6448.         GuiControl, Enable, LblScreenOffsetY
  6449.         GuiControl, Enable, ScreenOffsetY
  6450.     }
  6451.     return
  6452.  
  6453. MenuTray_About:
  6454.     IfNotEqual, FirstTimeA, No
  6455.         {
  6456.         Authors := GetContributors(0)
  6457.         RelVer := Globals.get("ReleaseVersion")
  6458.                 Gui, 2:+owner1 -Caption +Border
  6459.                 Gui, 2:Font, S10 CA03410,verdana
  6460.                 Gui, 2:Add, Text, x260 y27 w170 h20 Center, Release %RelVer%
  6461.                 Gui, 2:Add, Button, 0x8000 x316 y300 w70 h21, Close
  6462.                 Gui, 2:Add, Picture, 0x1000 x17 y16 w230 h180 gAboutDlg_Fishing, %A_ScriptDir%\data\splash.png
  6463.                 Gui, 2:Font, Underline C3571AC,verdana
  6464.                 Gui, 2:Add, Text, x260 y57 w170 h20 gVisitForumsThread Center, PoE forums thread
  6465.                 Gui, 2:Add, Text, x260 y87 w170 h20 gAboutDlg_AhkHome Center, AutoHotkey homepage
  6466.                 Gui, 2:Add, Text, x260 y117 w170 h20 gAboutDlg_GitHub Center, PoE-Item-Info GitHub
  6467.                 Gui, 2:Font, S7 CDefault normal, Verdana
  6468.                 Gui, 2:Add, Text, x16 y207 w410 h80,
  6469.                 (LTrim
  6470.         Shows affix breakdowns and other useful infos for any item or item link.
  6471.        
  6472.         Usage: Set PoE to Windowed Fullscreen mode and hover over any item or item link. Press Ctrl+C to show a tooltip.
  6473.        
  6474.         (c) %A_YYYY% Hazydoc, Nipper4369 and contributors:
  6475.         )
  6476.                 Gui, 2:Add, Text, x16 y277 w270 h80, %Authors%
  6477.        
  6478.                 FirstTimeA = No
  6479.         }
  6480.        
  6481.         Gui, 2:Show, h340 w435, About..
  6482.        
  6483.         ; Release counter animation
  6484.         tmpH = 0
  6485.         Loop, 20
  6486.         {
  6487.                 tmpH += 1
  6488.                 ControlMove, Static1,,,, %tmpH%, About..
  6489.                 Sleep, 100
  6490.         }
  6491.     return
  6492.  
  6493. AboutDlg_Fishing:
  6494.     ; See, GGG Chris, I have your best interests at heart. Hire me! :)
  6495.     MsgBox, 32, Did You Know?, Fishing is reel!
  6496.     return
  6497.    
  6498. AboutDlg_AhkHome:
  6499.         Run, http://ahkscript.org
  6500.     return
  6501.  
  6502. AboutDlg_GitHub:
  6503.     Run, http://github.com/andreberg/PoE-Item-Info
  6504.     return
  6505.    
  6506. VisitForumsThread:
  6507.     Run, http://www.pathofexile.com/forum/view-thread/790438
  6508.     return
  6509.  
  6510. 2ButtonClose:
  6511. 2GuiClose:
  6512.         WinGet, AbtWndID, ID, About..
  6513.         DllCall("AnimateWindow", "Int", AbtWndID, "Int", 500, "Int", 0x00090010)
  6514.         WinActivate, ahk_id %MainWndID%
  6515.     return
  6516.  
  6517. EditValuableUniques:
  6518.     OpenCreateDataTextFile("ValuableUniques.txt")
  6519.     return
  6520.  
  6521. EditValuableGems:
  6522.     OpenCreateDataTextFile("ValuableGems.txt")
  6523.     return
  6524.    
  6525. EditCurrencyRates:
  6526.     OpenCreateDataTextFile("CurrencyRates.txt")
  6527.     return
  6528.    
  6529. EditDropOnlyGems:
  6530.     OpenCreateDataTextFile("DropOnlyGems.txt")
  6531.     return
  6532.  
  6533. 3GuiClose:
  6534.     Gui, 3:Cancel
  6535.     return
  6536.  
  6537. UnhandledDlg_ShowItemText:
  6538.     Run, Notepad.exe
  6539.     WinActivate
  6540.     Send, ^v
  6541.     return
  6542.    
  6543. UnhandledDlg_OK:
  6544.     Gui, 3:Submit
  6545.     return
  6546.  
  6547. ; ############ ADD YOUR OWN MACROS HERE #############
  6548.  
  6549. /* #IfWinActive Path of Exile ahk_class Direct3DWindowClass ahk_exe PathOfExile.exe
  6550. {
  6551.    F2::SendEvent {Enter}/oos{Enter}
  6552. } */
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top