﻿ ; Path of Exile Item Info Tooltip

;

; Version: 1.9.2 (hazydoc / IGN:Sadou) Original Author

; Talisman Script added by (fuze / IGN:Dog)

; Script is currently maintained by various people and kept up to date by Bahnzo / IGN:Bahnzo

; This script was originally based on the POE_iLVL_DPS-Revealer script (v1.2d) found here:

; https://www.pathofexile.com/forum/view-thread/594346

; New Thread: https://www.pathofexile.com/forum/view-thread/790438

;

; Changes to the POE_iLVL_DPS-Revealer script as recent as it's version 1.4.1 have been

; brought over. Thank you Nipper4369 and Kislorod!

;

; The script has been added to substantially to enable the following features in addition to

; itemlevel and weapon DPS reveal:

;

; - show total affix statistic for rare items

; - show possible min-max ranges for all affixes on rare items

; - reveal the combination of difficult compound affixes (you might be surprised what you find)

; - show affix ranges for uniques

; - show map info (thank you, Kislorod and Necrolis)

; - show max socket info (thank you, Necrolis)

; - has the ability to convert currency items to chaos orbs (you can adjust the rates by editing

; <datadir>\CurrencyRates.txt)

; - can show which gems are valuable and/or drop-only (all user adjustable)

; - can show a reminder for uniques that are generally considered valuable (user adjustable as well)

; - adds a system tray icon and proper system tray description tooltip

;

; All of these features are user-adjustable by using a "database" of text files which come

; with the script and are easy to edit by non developers. See header comments in those files

; for format infos and data sources.

;

; Known issues:

;

; Even though there have been tons of tests made on composite affix combinations, I expect

; there to be edge cases still that may return an invalid or not found affix bracket.

; You can see these entries in the affix detail lines if they have the text "n/a" (not available)

; somewhere in them or if you see an empty range " - *". The star by the way marks ranges

; that have been added together for a guessed attempt as to the composition of a possible

; compound affix. If you see this star, take a closer look for a moment to check if the

; projection is correct. I expect these edge cases to be properly dealt with over time as the

; script matures. For now I'd estimate that at least 80% of the truly hard cases are correctly

; identified.

;

; Some background info: because the game concatenates values from multiple affix sources into

; one final entry on the ingame tooltip there is no reliable way to work backwards from the

; composite value to each individual part. For example, Stun Recovery can be added as suffix if

; it contributes alone, but can also be a prefix if it is a composite of Stun Recovery and

; Evasion Rating (or others). Because there is one final entry, while prefix and suffix can

; appear at the same time and will be added together, you can't reliably reverse engineer which

; affix contributed what part of the composite value. This is akin to taking a random source of

; numbers, adding them up to one value and then asking someone to work out backwards what the

; original source values were.

; Similarily, in cases like boosted Stun Recovery (1) and Evasion Rating (2) on an item in difficult

; cases there is no 100% reliable way to tell if the prefix "+ Evasion Rating / incr. Stun Recovery"

; contributed to both stats at once or if the suffix "+ Stun Recovery" contributed to (1)

; and the prefix "+ Evasion Rating" cotributed to (2) or possibly a combination of both.

; Often it is possible to make guesses by working your way backwards from both partial affixes, by

; looking at the affix bracket ranges and the item level to see what is even possible to be there and

; what isn't. In the worst case for a double compound affix, all four ranges will be possible to be

; combined.

;

; I have tested the tooltip on many, many items in game from my own stash and from trade chat

; and I can say that in the overwhelming majority of cases the tooltip does indeed work correctly.

;

; IMPORTANT: as you may know, the total amount of affixes (w/o implicit mods) can be 6, of which

; 3 at most are prefixes and likewise 3 at most are suffixes. Be especially weary, then of cases

; where this prefix/suffix limit is overcapped. It may happen that the tooltip shows 4 suffixes,

; and 3 prefixes total. In this case the most likely explanation is that the script failed to properly

; determine composite affixes. Composite affixes ("Comp. Prefix" or "Comp. Suffix" in the tooltip)

; are two affix lines on the ingame tooltip that together form one single composite affix.

; Edit v1.4: This hasn't happened for a longer time now, but I am leaving this important note in

; so end users stay vigilant (assuming anyone even reads this wall of text :)).

;

; - I do not know which affixes are affected by +% Item Quality. Currently I have functions in place

; that can boost a range or a single value to adjust for Item Quality but currently these aren't used

; much. Partially this is also because it is not easy to tell if out-of-bounds cases are the result

; of faulty input data (I initially pulled data from the PoE mods compendium but later made the PoE

; homepage the authoritative source overruling data from other sources) or of other unreckognized and

; unhandled entities or systems.

;

; Todo:

;

; - handle ranges for implicit mods

; - find a way to deal with master crafted mods (currently that's a tough one, probably won't be possible)

; - show max possible for guesstimated ranges

; - de-globalize the script (almost done)

; - refactor ParseAffixes into ParseAffixesSimple and ParseAffixesComplex (low priority)

;

; Slinkston edit for Todo for 2.0 additions for hazydoc or someone else knowledgable in coding:

; - FYI: All of the stuff I have edited has been marked with ; Slinkston edit. Some may need to be cleaned up or redone if

; they are done improperly/sloppy. I have tested all changes with stuff in my stash and with friends, but not every single possibility.

; - Accuracy is a nightmare. Anyhow, "of the Assassin - 321 to 360 Accuracy (80) (Bow and Wand)" needs

; to be addressed for 2.0 or not /shrug. I have passed on the request to GGG to perhaps mark up their affixes so they are decipherable.

; - Uniques need to be updated. Apparently poe_scrape.py has an error and is unable to run until fixed.

; - Valuable Gems and Valuable Uniques needs to be updated as well. Will work on this keeping in mind both default leagues and temp leagues

; - Divination card info would be great such as a) what you can possibly get for the collection, b) where that card drops, and c) what supporter

; created it (if known).

; - Jewel support for min/max rolls and what is a suffix and what is a prefix so you know what you may be able to exalt. 9/15/2015 - I just noticed that

; GGG added jewel affixes, both prefix and suffix, for jewels to their item database.

; - Legacy item alert on the item would be useful for those players that take breaks and come back without reading all the patch notes and/or

; not recognizing some item may have changed or not. This alert can be placed along the bottom with 'quality, valuable, mirrored, etc.'

; I imagine that this would not be hard to do, but would require a lot of small detail work. Because all uniques are nerfed/buffed in

; specific ways, there is no 'quick' and easy way to do this. There would have to be a specific check for each specific unique item looking

; at the particular change(s) and compare it to the existing known unique setup vs the legacy setup. I would be willing to do all the small

; detail work required for each unique if someone would write the code required for this to work and how this would work with the current unique.txt

; list. This is obviously less valuable of an addition to the PoE-Item-Info script than general upgrades/div cards/jewel support.

;

; Notes:

;

; - Global values marked with an inline comment "d" are globals for debugging so they can be easily

; (re-)enabled using global search and replace. Marking variables as global means they will show

; up in AHK's Variables and contents view of the script.

;

; Needs AutoHotKey v1.1.05 or later

; from http://ahkscript.org and NOT http://www.autohotkey.com

; the latter domain was apparently taken over by a for-profit company!

;

; Original credits:

;

; mcpower - for the base iLVL display of the script 5months ago before Immo.

; Immo - for the base iLVL display of the script.(Which was taken from mcpower.)

; olop4444 - for helping me figure out the calculations for Q20 items.

; Aeons - for a rewrite and fancy tooltips.

; kongyuyu - for base item level display.

; Fayted - for testing the script.

;

; Original author's comment:

;

; If you have any questions or comments please post them there as well. If you think you can help

; improve this project. I am looking for contributors. So Pm me if you think you can help.

;

; If you have a issue please post what version you are using.

; Reason being is that something that might be a issue might already be fixed.

;

; Run test suites (see end of script)

; Note: don't set this to true for normal every day use...

; This is just for fellow developers.

RunTests := False

#SingleInstance force

#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.

#Persistent ; Stay open in background

SendMode Input ; Recommended for new scripts due to its superior speed and reliability.

#Include %A_ScriptDir% \data\Version . txt

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."

If ( A_AhkVersion <= AHKVersionRequired )

{

MsgBox , 16 , Wrong AutoHotkey Version , % MsgWrongAHKVersion

ExitApp

}

#Include %A_ScriptDir% \data\Messages . txt

; Instead of polluting the default namespace with Globals, create our own Globals "namespace".

class Globals {

Set ( name , value ) {

Globals [ name ] := value

}

Get ( name , value_default = "" ) {

result := Globals [ name ]

If ( result == "" ) {

result := value_default

}

return result

}

}

Globals . Set ( "AHKVersionRequired" , AHKVersionRequired )

Globals . Set ( "ReleaseVersion" , ReleaseVersion )

Globals . Set ( "DataDir" , A_ScriptDir . "\data" )

class UserOptions {

OnlyActiveIfPOEIsFront := 1 ; Set to 1 to make it so the script does nothing if Path of Exile window isn't the frontmost.

; If 0, the script also works if PoE isn't frontmost. This is handy for have the script parse

; textual item representations appearing somewhere else, like in the forums or text files.

ShowItemLevel := 1 ; Show item level and the item type's base level (enabled by default change to 0 to disable)

ShowMaxSockets := 1 ; Show the max sockets based on ilvl and type

ShowDamageCalculations := 1 ; Show damage projections (for weapons only)

ShowAffixTotals := 1 ; Show total affix statistics

ShowAffixDetails := 1 ; Show detailed info about affixes

ShowAffixLevel := 0 ; Show item level of the affix

ShowAffixBracket := 1 ; Show range for the affix' bracket as is on the item

ShowAffixMaxPossible := 1 ; Show max possible bracket for an affix based on the item's item level

ShowAffixBracketTier := 1 ; Show a T# indicator of the tier the affix bracket is in.

; T1 being the highest possible, T2 second-to-highest and so on

ShowAffixBracketTierTotal := 1 ; Appends the total number of tiers for a given affix in parentheses T/#Total

; T4/8 would represent the fourth highest tier, in eight total tiers.

ShowDarkShrineInfo := 0 ; Appends info about DarkShrine effects of affixes to rares

TierRelativeToItemLevel := 0 ; When determining the affix bracket tier, take item level into consideration.

; However, this also means that the lower the item level the less the diversity

; of possible affix tiers since there aren't as many possibilities. This will

; give the illusion that a low level item might be really, really good when it

; has all T1 but in reality it can only have T1 since it's item level is so low

; it can only ever take the first bracket.

;

; If this option is set to 0, the tiers will always display relative to the full

; range of tiers available, ignoring the item level.

ShowCurrencyValueInChaos := 1 ; Convert the value of currency items into chaos orbs.

; This is based on the rates defined in <datadir>\CurrencyRates.txt

; You should edit this file with the current currency rates.

ShowUniqueEvaluation := 1 ; Display reminder when a unique is valuable.

; This is based on <datadir>\ValuableUniques.txt

; You can edit this file to suit your own needs.

ShowGemEvaluation := 1 ; Display reminder when a gem is valuable and/or drop only.

; This is based on <datadir>\ValuableGems.txt and <datadir>\DropOnlyGems.txt

; You can edit these files to suit your own needs.

GemQualityValueThreshold := 10 ; If the gem's added quality exceeds this value, consider it valuable regardless of which gem it is.

MaxSpanStartingFromFirst := 1 ; When showing max possible, don't just show the highest possible affix bracket

; but construct a pseudo range which spans the lower bound of the lowest possible

; bracket to the upper bound of the highest possible one.

;

; This is usually what you want to see when evaluating an item's worth. The exception

; being when you want to reroll an affix to the highest possible value within it's

; current bracket - then you need to see the affix range that is actually on the item

; right now.

CompactDoubleRanges := 1 ; Show double ranges as "1-172" instead of "1-8 to 160-172"

CompactAffixTypes := 1 ; Use compact affix type designations: Suffix = S, Prefix = P, Comp. Suffix = CS, Comp. Prefix = CP

MarkHighLinksAsValuable := 1 ; Mark rares or uniques with 5L or 6L as valuable.

MirrorAffixLines := 1 ; Show a copy of the affix line in question when showing affix details.

;

; For example, would display "Prefix, 5-250" instead of "+246 to Accuracy Rating, Prefix, 5-250".

; Since the affixes are processed in order one can attribute which is which to the ordering of

; the lines in the tooltip to the item data in game.

MirrorLineFieldWidth := 18 ; Mirrored affix line width. Set to a number above 0 to truncate (or pad) to this many characters.

; Appends AffixDetailEllipsis when truncating.

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.

;

; Keep in mind that there are sometimes double ranges to be displayed. Like for example on an axe, implicit physical damage might

; 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,

; and at most a 3 digit maximum value. To then display just the lower bound (which constitutes one value range field), you would need

; 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)

; 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

; is per field, so a value of 8 can display two 8 character wide fields correctly.

AffixDetailDelimiter := " " ; Field delimiter for affix detail lines. This is put between value range fields. If this value were set to a comma, the above

; double range example would become 132-179,168-189.

AffixDetailEllipsis := "…" ; If the MirrorLineFieldWidth is set to a value that is smaller than the actual length of the affix line text

; the affix line will be cut off and this text will be appended at the end to indicate tha the line was truncated.

;

; Usually this is set to the ASCII or Unicode value of the three dot ellipsis (alt code: 0133).

; Note that the correct display of text characters outside the ASCII standard depend on the file encoding and the

; AHK version used. For best results, save this file as ANSI encoding which can be read and displayed correctly by

; either ANSI based AutoHotkey or Unicode based AutoHotkey.

;

; Example: assume the affix line to be mirrored is '+#% increased Spell Damage'.

; If the MirrorLineFieldWidth is set to 18, this field would be shown as '+#% increased Spel…'

PutResultsOnClipboard := 0 ; Put result text on clipboard (overwriting the textual representation the game put there to begin with)

; Pixels mouse must move to auto-dismiss tooltip

MouseMoveThreshold := 40

; Set this to 1 if you want to have the tooltip disappear after the time frame set below.

; Otherwise you will have to move the mouse by 5 pixels for the tip to disappear.

UseTooltipTimeout := 0

;How many ticks to wait before removing tooltip. 1 tick = 100ms. Example, 50 ticks = 5secends, 75 Ticks = 7.5Secends

ToolTipTimeoutTicks := 150

; Font size for the tooltip, leave empty for default

FontSize := 11

; Displays the tooltip in virtual screen space at fixed coordinates.

; Virtual screen space means the complete desktop frame, including any secondary monitors.

DisplayToolTipAtFixedCoords := 0

; Coordinates relative to top left corner, increasing by going down and to the right.

; Only used if DisplayToolTipAtFixedCoords is 1.

ScreenOffsetX := 0

ScreenOffsetY := 0

ScanUI ( )

{

this . OnlyActiveIfPOEIsFront := GuiGet ( "OnlyActiveIfPOEIsFront" )

this . ShowItemLevel := GuiGet ( "ShowItemLevel" )

this . ShowMaxSockets := GuiGet ( "ShowMaxSockets" )

this . ShowDamageCalculations := GuiGet ( "ShowDamageCalculations" )

this . ShowAffixTotals := GuiGet ( "ShowAffixTotals" )

this . ShowAffixDetails := GuiGet ( "ShowAffixDetails" )

this . ShowAffixLevel := GuiGet ( "ShowAffixLevel" )

this . ShowAffixBracket := GuiGet ( "ShowAffixBracket" )

this . ShowAffixMaxPossible := GuiGet ( "ShowAffixMaxPossible" )

this . ShowAffixBracketTier := GuiGet ( "ShowAffixBracketTier" )

this . ShowAffixBracketTierTotal := GuiGet ( "ShowAffixBracketTierTotal" )

this . TierRelativeToItemLevel := GuiGet ( "TierRelativeToItemLevel" )

this . ShowDarkShrineInfo := GuiGet ( "ShowDarkShrineInfo" )

this . ShowCurrencyValueInChaos := GuiGet ( "ShowCurrencyValueInChaos" )

this . DisplayToolTipAtFixedCoords := GuiGet ( "DisplayToolTipAtFixedCoords" )

this . ScreenOffsetX := GuiGet ( "ScreenOffsetX" )

this . ScreenOffsetY := GuiGet ( "ScreenOffsetY" )

this . ShowUniqueEvaluation := GuiGet ( "ShowUniqueEvaluation" )

this . ShowGemEvaluation := GuiGet ( "ShowGemEvaluation" )

this . GemQualityValueThreshold := GuiGet ( "GemQualityValueThreshold" )

this . MaxSpanStartingFromFirst := GuiGet ( "MaxSpanStartingFromFirst" )

this . CompactDoubleRanges := GuiGet ( "CompactDoubleRanges" )

this . CompactAffixTypes := GuiGet ( "CompactAffixTypes" )

this . MarkHighLinksAsValuable := GuiGet ( "MarkHighLinksAsValuable" )

this . MirrorAffixLines := GuiGet ( "MirrorAffixLines" )

this . MirrorLineFieldWidth := GuiGet ( "MirrorLineFieldWidth" )

this . ValueRangeFieldWidth := GuiGet ( "ValueRangeFieldWidth" )

this . AffixDetailDelimiter := GuiGet ( "AffixDetailDelimiter" )

this . AffixDetailEllipsis := GuiGet ( "AffixDetailEllipsis" )

this . PutResultsOnClipboard := GuiGet ( "PutResultsOnClipboard" )

this . MouseMoveThreshold := GuiGet ( "MouseMoveThreshold" )

this . UseTooltipTimeout := GuiGet ( "UseTooltipTimeout" )

this . ToolTipTimeoutTicks := GuiGet ( "ToolTipTimeoutTicks" )

this . FontSize := GuiGet ( "FontSize" )

}

}

Opts := new UserOptions ( )

class Fonts {

Init ( FontSizeFixed , FontSizeUI )

{

this . FontSizeFixed := FontSizeFixed

this . FontSizeUI := FontSizeUI

this . FixedFont := this . CreateFixedFont ( FontSizeFixed )

this . UIFont := this . CreateUIFont ( FontSizeUI )

}

CreateFixedFont ( FontSize_ )

{

Options :=

If ( ! ( FontSize_ == "" ) )

{

Options = s %FontSize_%

}

Gui Font , %Options% , Courier New

Gui Font , %Options% , Consolas

Gui Add , Text , HwndHidden ,

SendMessage , 0x31 ,,,, ahk_id %Hidden%

return ErrorLevel

}

CreateUIFont ( FontSize_ )

{

Options :=

If ( ! ( FontSize_ == "" ) )

{

Options = s %FontSize_%

}

Gui Font , %Options% , Tahoma

Gui Font , %Options% , Segoe UI

Gui Add , Text , HwndHidden ,

SendMessage , 0x31 ,,,, ahk_id %Hidden%

return ErrorLevel

}

Set ( NewFont )

{

AhkExe := GetAhkExeFilename ( )

SendMessage , 0x30 , NewFont , 1 ,, ahk_class tooltips_class32 ahk_exe %AhkExe%

; Development versions of AHK

SendMessage , 0x30 , NewFont , 1 ,, ahk_class tooltips_class32 ahk_exe AutoHotkeyA32 . exe

SendMessage , 0x30 , NewFont , 1 ,, ahk_class tooltips_class32 ahk_exe AutoHotkeyU32 . exe

SendMessage , 0x30 , NewFont , 1 ,, ahk_class tooltips_class32 ahk_exe AutoHotkeyU64 . exe

}

SetFixedFont ( FontSize_ =- 1 )

{

If ( FontSize_ == - 1 )

{

FontSize_ := this . FontSizeFixed

}

Else

{

this . FontSizeFixed := FontSize_

this . FixedFont := this . CreateFixedFont ( FontSize_ )

}

this . Set ( this . FixedFont )

}

SetUIFont ( FontSize_ =- 1 )

{

If ( FontSize_ == - 1 )

{

FontSize_ := this . FontSizeUI

}

Else

{

this . FontSizeUI := FontSize_

this . UIFont := this . CreateUIFont ( FontSize_ )

}

this . Set ( this . UIFont )

}

GetFixedFont ( )

{

return this . FixedFont

}

GetUIFont ( )

{

return this . UIFont

}

}

class ItemData_ {

Links := ""

Stats := ""

NamePlate := ""

Affixes := ""

FullText := ""

IndexAffixes := - 1

IndexLast := - 1

PartsLast := ""

Rarity := ""

Parts := [ ]

ClearParts ( )

{

Loop , % this . Parts . MaxIndex ( )

{

this . Parts . Remove ( this . Parts . MaxIndex ( ) )

}

}

}

ItemData := new ItemData_ ( )

class Item {

Name := ""

TypeName := ""

Quality := ""

BaseLevel := ""

RarityLevel := ""

BaseType := ""

SubType := ""

GripType := ""

Level := ""

MapLevel := ""

MaxSockets := ""

IsUnidentified := ""

IsCorrupted := ""

IsGem := ""

IsCurrency := ""

IsUnique := ""

IsRare := ""

IsBow := ""

IsFlask := ""

IsBelt := ""

IsRing := ""

IsUnsetRing := ""

IsAmulet := ""

IsSingleSocket := ""

IsFourSocket := ""

IsThreeSocket := ""

IsQuiver := ""

IsWeapon := ""

IsMap := ""

IsMirrored := ""

HasEffect := ""

}

Item := new Item ( )

class AffixTotals_ {

NumPrefixes := 0

NumSuffixes := 0

NumTotals := 0

Reset ( )

{

this . NumPrefixes := 0

this . NumSuffixes := 0

this . NumTotals := 0

}

}

AffixTotals := new AffixTotals_ ( )

class AffixLines_ {

__New ( )

{

this . Length := 0

}

; Sets fields to empty string

Clear ( Index )

{

this [ Index ] := ""

}

ClearAll ( )

{

Loop , % this . MaxIndex ( )

{

this . Clear ( A_Index )

}

}

; Actually removes fields

Reset ( )

{

Loop , % this . MaxIndex ( )

{

this . Remove ( this . MaxIndex ( ) )

}

this . Length := 0

}

Set ( Index , Contents )

{

this [ Index ] := Contents

this . Length := this . MaxIndex ( )

}

}

AffixLines := new AffixLines_ ( )

IfNotExist , %A_ScriptDir% \config . ini

{

IfNotExist , %A_ScriptDir% \data\defaults . ini

{

CreateDefaultConfig ( )

}

CopyDefaultConfig ( )

}

; Windows system tray icon

; possible values: poe.ico, poe-bw.ico, poe-web.ico, info.ico

; set before creating the settings UI so it gets used for the settigns dialog as well

Menu , Tray , Icon , %A_ScriptDir% \data\poe - bw . ico

ReadConfig ( )

Sleep , 100

CreateSettingsUI ( )

Menu , TextFiles , Add , Valuable Uniques , EditValuableUniques

Menu , TextFiles , Add , Valuable Gems , EditValuableGems

Menu , TextFiles , Add , Dropy Only Gems , EditDropOnlyGems

Menu , TextFiles , Add , Currency Rates , EditCurrencyRates

; Menu tooltip

RelVer := Globals . Get ( "ReleaseVersion" )

Menu , Tray , Tip , Path of Exile Item Info %RelVer%

Menu , Tray , NoStandard

Menu , Tray , Add , About ..., MenuTray_About

Menu , Tray , Add , PoE Item Info Settings , ShowSettingsUI

Menu , Tray , Add ; Separator

Menu , Tray , Add , Edit , :TextFiles

Menu , Tray , Add ; Separator

Menu , Tray , Standard

Menu , Tray , Default , PoE Item Info Settings

IfNotExist , %A_ScriptDir% \data

{

MsgBox , 16 , % Msg . DataDirNotFound

exit

}

#Include %A_ScriptDir% \data\MapList . txt

Fonts . Init ( Opts . FontSize , 9 )

GetAhkExeFilename ( Default_ = "AutoHotkey.exe" )

{

AhkExeFilename := Default_

If ( A_AhkPath )

{

StringSplit , AhkPathParts , A_AhkPath , \

Loop , % AhkPathParts0

{

IfInString , AhkPathParts %A_Index% , . exe

{

AhkExeFilename := AhkPathParts %A_Index%

Break

}

}

}

return AhkExeFilename

}

OpenCreateDataTextFile ( Filename )

{

Filepath := A_ScriptDir . "\data\" . Filename

IfExist , % Filepath

{

Run , % Filepath

}

Else

{

File := FileOpen ( Filepath , "w" )

if !IsObject ( File )

{

MsgBox , 16 , Can't create %A_ScriptDir% \data\ValuableUniques . txt

return

}

File . Close ( )

Run , % Filepath

}

return

Run , %A_ScriptDir% \data\ %Filename%

return

}

ParseElementalDamage ( String , DmgType , ByRef DmgLo , ByRef DmgHi )

{

IfInString , String , %DmgType% Damage

{

IfInString , String , Converted to or IfInString , String , taken as

{

return

}

IfNotInString , String , increased

{

StringSplit , Arr , String , %A_Space%

StringSplit , Arr , Arr2 , -

DmgLo := Arr1

DmgHi := Arr2

}

}

}

; Function that checks item type name against entries

; from ItemList.txt to get the item's base level

; Added by kongyuyu, changed by hazydoc

CheckBaseLevel ( ItemTypeName )

{

; Added to correctly id Superior items

; code by sirmanky

If ( InStr ( ItemTypeName , "Superior" ) == 1 )

ItemTypeName := SubStr ( ItemTypeName , 10 )

ItemListArray = 0

Loop , Read , %A_ScriptDir% \data\ItemList . txt

{

; This loop retrieves each line from the file, one at a time.

ItemListArray += 1 ; Keep track of how many items are in the array.

StringSplit , NameLevel , A_LoopReadLine , | ,

Array %ItemListArray% 1 := NameLevel1 ; Store this line in the next array element.

Array %ItemListArray% 2 := NameLevel2

}

Loop %ItemListArray% {

element := Array %A_Index% 1

;original line restored by Bahnzo to restore Base Item Level

;IfInString, ItemTypeName, %element%

If ( ItemTypeName == element )

{

BaseLevel := Array %A_Index% 2

Break

}

}

return BaseLevel

}

CheckRarityLevel ( RarityString )

{

IfInString , RarityString , Normal

return 1

IfInString , RarityString , Magic

return 2

IfInString , RarityString , Rare

return 3

IfInString , RarityString , Unique

return 4

return 0 ; unknown rarity. shouldn't happen!

}

ParseItemType ( ItemDataStats , ItemDataNamePlate , ByRef BaseType , ByRef SubType , ByRef GripType )

{

; Grip type only matters for weapons at this point. For all others it will be 'None'.

GripType = None

; Check stats section first as weapons usually have their sub type as first line

Loop , Parse , ItemDataStats , `n , `r

{

IfInString , A_LoopField , One Handed Axe

{

BaseType = Weapon

SubType = Axe

GripType = 1H

return

}

IfInString , A_LoopField , Two Handed Axe

{

BaseType = Weapon

SubType = Axe

GripType = 2H

return

}

IfInString , A_LoopField , One Handed Mace

{

BaseType = Weapon

SubType = Mace

GripType = 1H

return

}

IfInString , A_LoopField , Two Handed Mace

{

BaseType = Weapon

SubType = Mace

GripType = 2H

return

}

IfInString , A_LoopField , Sceptre

{

BaseType = Weapon

SubType = Sceptre

GripType = 1H

return

}

IfInString , A_LoopField , Staff

{

BaseType = Weapon

SubType = Staff

GripType = 2H

return

}

IfInString , A_LoopField , One Handed Sword

{

BaseType = Weapon

SubType = Sword

GripType = 1H

return

}

IfInString , A_LoopField , Two Handed Sword

{

BaseType = Weapon

SubType = Sword

GripType = 2H

return

}

IfInString , A_LoopField , Dagger

{

BaseType = Weapon

SubType = Dagger

GripType = 1H

return

}

IfInString , A_LoopField , Claw

{

BaseType = Weapon

SubType = Claw

GripType = 1H

return

}

IfInString , A_LoopField , Bow

{

; Not really sure if I should classify bow as 2H (because that would make sense)

; but you can equip a quiver in 2nd hand slot, so it could be 1H?

BaseType = Weapon

SubType = Bow

GripType = 1H

return

}

IfInString , A_LoopField , Wand

{

BaseType = Weapon

SubType = Wand

GripType = 1H

return

}

}

; Check name plate section

Loop , Parse , ItemDataNamePlate , `n , `r

{

; a few cases that cause incorrect id later

; and thus should come first

; Note: still need to work on proper id for

; all armour types.

IfInString , A_LoopField , Ringmail Gloves

{

BaseType = Armour

SubType = Gloves

return

}

IfInString , A_LoopField , Ringmail Boots

{

BaseType = Armour

SubType = Gloves

return

}

If ( RegExMatch ( A_LoopField , "Ringmail$" ) )

{

BaseType = Armour

SubType = BodyArmour

return

}

IfInString , A_LoopField , Mantle

{

BaseType = Armour

SubType = BodyArmour

return

}

IfInString , A_LoopField , Shell

{

BaseType = Armour

SubType = BodyArmour

return

}

; Belts, Amulets, Rings, Quivers, Flasks

IfInString , A_LoopField , Rustic Sash

{

BaseType = Item

SubType = Belt

return

}

IfInString , A_LoopField , Belt

{

BaseType = Item

SubType = Belt

return

}

IfInString , A_LoopField , Amulet

{

BaseType = Item

SubType = Amulet

return

}

If ( RegExMatch ( A_LoopField , "\bRing\b" ) )

{

BaseType = Item

SubType = Ring

return

}

IfInString , A_LoopField , Quiver

{

BaseType = Item

SubType = Quiver

return

}

IfInString , A_LoopField , Flask

{

BaseType = Item

SubType = Flask

return

}

IfInString , A_LoopField , %A_Space% Map

{

Global matchList

BaseType = Map

Loop % matchList . MaxIndex ( )

{

Match := matchList [ A_Index ]

IfInString , A_LoopField , %Match%

{

SubType = %Match%

return

}

}

SubType = Unknown %A_Space% Map

return

}

; Dry Peninsula fix

IfInString , A_LoopField , Dry %A_Space% Peninsula

{

BaseType = Map

SubType = Dry %A_Space% Peninsula

return

}

; Jewels

IfInString , A_LoopField , Cobalt %A_Space% Jewel

{

BaseType = Jewel

SubType = Cobalt Jewel

return

}

IfInString , A_LoopField , Crimson %A_Space% Jewel

{

BaseType = Jewel

SubType = Crimson Jewel

return

}

IfInString , A_LoopField , Viridian %A_Space% Jewel

{

BaseType = Jewel

SubType = Viridian Jewel

return

}

; Shields

IfInString , A_LoopField , Shield

{

BaseType = Armour

SubType = Shield

return

}

IfInString , A_LoopField , Buckler

{

BaseType = Armour

SubType = Shield

return

}

IfInString , A_LoopField , Bundle

{

BaseType = Armour

SubType = Shield

return

}

IfInString , A_LoopField , Gloves

{

BaseType = Armour

SubType = Gloves

return

}

IfInString , A_LoopField , Mitts

{

BaseType = Armour

SubType = Gloves

return

}

IfInString , A_LoopField , Gauntlets

{

BaseType = Armour

SubType = Gloves

return

}

; Helmets

IfInString , A_LoopField , Helmet

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Helm

{

BaseType = Armour

SubType = Helmet

return

}

If ( InStr ( A_LoopField , "Hat" ) and ( Not InStr ( A_LoopField , "Hate" ) ) )

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Mask

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Hood

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Ursine Pelt

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Lion Pelt

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Circlet

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Sallet

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Burgonet

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Bascinet

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Crown

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Cage

{

BaseType = Armour

SubType = Helmet

return

}

IfInString , A_LoopField , Tricorne

{

BaseType = Armour

SubType = Helmet

return

}

; Boots

IfInString , A_LoopField , Boots

{

BaseType = Armour

SubType = Boots

return

}

IfInString , A_LoopField , Greaves

{

BaseType = Armour

SubType = Boots

return

}

IfInString , A_LoopField , Slippers

{

BaseType = Armour

SubType = Boots

return

}

}

; TODO: need a reliable way to determine sub type for armour

; right now it's just determine anything else first if it's

; not that, it's armour.

BaseType = Armour

SubType = Armour

}

GetClipboardContents ( DropNewlines = False )

{

Result =

If Not DropNewlines

{

Loop , Parse , Clipboard , `n , `r

{

Result := Result . A_LoopField . "`r`n"

}

}

Else

{

Loop , Parse , Clipboard , `n , `r

{

Result := Result . A_LoopField

}

}

return Result

}

SetClipboardContents ( String )

{

Clipboard := String

}

; Splits StrInput on StrDelimiter. Returns an object that has a 'length' field

; containing the number of parts and 0 .. (length) fields containing the substrings.

; Example: if parts is the object returned by this function, then

; 'parts.length' gives the number of parts

; 'parts[1]' gives the first part (if there is one)

; Note: if StrDelimiter is not present in StrInput, length == 1 and parts[1] == StrInput

; Note2: as per AHK docs, parts.(Min|Max)Index() also work of course.

SplitString ( StrInput , StrDelimiter )

{

TempDelim := "``"

Chunks := Object ( )

StringReplace , TempResult , StrInput , %StrDelimiter% , %TempDelim% , All

StringSplit , Parts , TempResult , %TempDelim%

Chunks [ "length" ] := Parts0

Loop , %Parts0%

{

Chunks [ A_Index ] := Parts %A_Index%

}

return Chunks

}

; TODO: LookupAffixBracket and LookupAffixData contain a lot of duplicate code.

; Look up just the most applicable bracket for an affix.

; Most applicable means Value is between bounds of bracket range or

; highest entry possible given the item level.

;

; Returns "#-#" format range

;

; If Value is unspecified ("") return the max possible

; bracket based on item level

LookupAffixBracket ( Filename , ItemLevel , Value = "" , ByRef BracketLevel = "" , ByRef BracketIndex = 0 )

{

AffixLevel := 0

AffixDataIndex := 0

If ( Not Value == "" )

{

ValueLo := Value ; Value from ingame tooltip

ValueHi := Value ; For single values (which most of them are) ValueLo == ValueHi

ParseRange ( Value , ValueHi , ValueLo )

}

LookupIsDoubleRange := False ; For affixes like "Adds +# ... Damage" which have a lower and an upper bound range

BracketRange := "n/a"

Loop , Read , %A_ScriptDir% \ %Filename%

{

AffixDataIndex += 1

StringSplit , AffixDataParts , A_LoopReadLine , | ,

RangeLevel := AffixDataParts1

RangeValues := AffixDataParts2

If ( RangeLevel > ItemLevel )

{

AffixDataIndex -= 1 ; Since we added 1 above, before we noticed range level is above item level

Break

}

IfInString , RangeValues , ` ,

{

LookupIsDoubleRange := True

}

If ( LookupIsDoubleRange )

{

; Example lines from txt file database for double range lookups:

; 3|1,14-15

; 13|1-3,35-37

StringSplit , DoubleRangeParts , RangeValues , ` ,

LB := DoubleRangeParts %DoubleRangeParts% 1

UB := DoubleRangeParts %DoubleRangeParts% 2

; Default case: lower bound is single value: #

; see level 3 case in example lines above

LBMin := LB

LBMax := LB

UBMin := UB

UBMax := UB

IfInString , LB , -

{

; Lower bound is a range: #-#q

ParseRange ( LB , LBMax , LBMin )

}

IfInString , UB , -

{

ParseRange ( UB , UBMax , UBMin )

}

LBPart = %LBMin%

UBPart = %UBMax%

; Record bracket range if it is within bounds of the text file entry

If ( Value == "" or ( ( ( ValueLo >= LBMin ) and ( ValueLo <= LBMax ) ) and ( ( ValueHi >= UBMin ) and ( ValueHi <= UBMax ) ) ) )

{

BracketRange = %LBPart% - %UBPart%

AffixLevel = %RangeLevel%

}

}

Else

{

ParseRange ( RangeValues , HiVal , LoVal )

; Record bracket range if it is within bounds of the text file entry

If ( Value == "" or ( ( ValueLo >= LoVal ) and ( ValueHi <= HiVal ) ) )

{

BracketRange = %LoVal% - %HiVal%

AffixLevel = %RangeLevel%

}

}

If ( Value == "" )

{

AffixLevel = %RangeLevel%

}

}

BracketIndex := AffixDataIndex

BracketLevel := AffixLevel

return BracketRange

}

; Look up complete data for an affix. Depending on settings flags

; this may include many things, and will return a string used for

; end user display rather than further calculations.

; Use LookupAffixBracket if you need a range format to do calculations with.

LookupAffixData ( Filename , ItemLevel , Value , ByRef BracketLevel = "" , ByRef Tier = 0 )

{

Global Opts

AffixLevel := 0

AffixDataIndex := 0

ValueLo := Value ; Value from ingame tooltip

ValueHi := Value ; For single values (which most of them are) ValueLo == ValueHi

ValueIsMinMax := False ; Treat Value as min/max units (#-#) or as single unit (#)

LookupIsDoubleRange := False ; For affixes like "Adds +# ... Damage" which have a lower and an upper bound range

FirstRangeValues =

BracketRange := "n/a"

MaxRange =

FinalRange =

MaxLevel := 1

RangeLevel := 1

Tier := 0

MaxTier := 0

IfInString , Value , -

{

ParseRange ( Value , ValueHi , ValueLo )

ValueIsMinMax := True

}

; TODO refactor pre-pass into its own method

; Pre-pass to determine max tier

Loop , Read , %A_ScriptDir% \ %Filename%

{

StringSplit , AffixDataParts , A_LoopReadLine , | ,

RangeLevel := AffixDataParts1

If ( Globals . Get ( "TierRelativeToItemLevelOverride" , Opts . TierRelativeToItemLevel ) and ( RangeLevel > ItemLevel ) )

{

Break

}

; Yes, this is correct incrementing MaxTier here and not before the break!

MaxTier += 1

}

Loop , Read , %A_ScriptDir% \ %Filename%

{

AffixDataIndex += 1

StringSplit , AffixDataParts , A_LoopReadLine , | ,

RangeValues := AffixDataParts2

RangeLevel := AffixDataParts1

If ( AffixDataIndex == 1 )

{

FirstRangeValues := RangeValues

}

If ( Globals . Get ( "TierRelativeToItemLevelOverride" , Opts . TierRelativeToItemLevel ) and ( RangeLevel > ItemLevel ) )

{

Break

}

MaxLevel := RangeLevel

IfInString , RangeValues , ` ,

{

LookupIsDoubleRange := True

}

If ( LookupIsDoubleRange )

{

; Variables for min/max double ranges, like in the "Adds +# ... Damage" case

; Global LBMin ; (L)ower (B)ound minium value

; Global LBMax ; (L)ower (B)ound maximum value

; GLobal UBMin ; (U)pper (B)ound minimum value

; GLobal UBMax ; (U)pper (B)ound maximum value

; ; same, just for the first range's values

; Global FRLBMin

; Global FRLBMax

; Global FRUBMin

; Global FRUBMax

; Example lines from txt file database for double range lookups:

; 3|1,14-15

; 13|1-3,35-37

StringSplit , DoubleRangeParts , RangeValues , ` ,

LB := DoubleRangeParts %DoubleRangeParts% 1

UB := DoubleRangeParts %DoubleRangeParts% 2

; Default case: lower bound is single value: #

; see level 3 case in example lines above

LBMin := LB

LBMax := LB

UBMin := UB

UBMax := UB

IfInString , LB , -

{

; Lower bound is a range: #-#

ParseRange ( LB , LBMax , LBMin )

}

IfInString , UB , -

{

ParseRange ( UB , UBMax , UBMin )

}

If ( AffixDataIndex == 1 )

{

StringSplit , FirstDoubleRangeParts , FirstRangeValues , ` ,

FRLB := FirstDoubleRangeParts %FirstDoubleRangeParts% 1

FRUB := FirstDoubleRangeParts %FirstDoubleRangeParts% 2

ParseRange ( FRUB , FRUBMax , FRUBMin )

ParseRange ( FRLB , FRLBMax , FRLBMin )

}

If ( ( LBMin == LBMax ) or Opts . CompactDoubleRanges )

{

LBPart = %LBMin%

}

Else

{

LBPart = %LBMin% - %LBMax%

}

If ( ( UBMin == UBMax ) or Opts . CompactDoubleRanges )

{

UBPart = %UBMax%

}

Else

{

UBPart = %UBMin% - %UBMax%

}

If ( ( FRLBMin == FRLBMax ) or Opts . CompactDoubleRanges )

{

FRLBPart = %FRLBMin%

}

Else

{

FRLBPart = %FRLBMin% - %FRLBMax%

}

If ( Opts . CompactDoubleRanges )

{

MiddlePart := "-"

}

Else

{

MiddlePart := " to "

}

; Record bracket range if it is withing bounds of the text file entry

If ( ( ( ValueLo >= LBMin ) and ( ValueLo <= LBMax ) ) and ( ( ValueHi >= UBMin ) and ( ValueHi <= UBMax ) ) )

{

BracketRange = %LBPart% %MiddlePart% %UBPart%

AffixLevel = %MaxLevel%

Tier := ( ( MaxTier - AffixDataIndex ) + 1 )

If ( Opts . ShowAffixBracketTierTotal )

{

Tier := Tier . "/" . MaxTier

}

}

; Record max possible range regardless of within bounds

If ( Opts . MaxSpanStartingFromFirst )

{

MaxRange = %FRLBPart% %MiddlePart% %UBPart%

}

Else

{

MaxRange = %LBPart% %MiddlePart% %UBPart%

}

}

Else

{

If ( AffixDataIndex = 1 )

{

ParseRange ( FirstRangeValues , FRHiVal , FRLoVal )

}

ParseRange ( RangeValues , HiVal , LoVal )

; Record bracket range if it is within bounds of the text file entry

If ( ( ValueLo >= LoVal ) and ( ValueHi <= HiVal ) )

{

If ( LoVal = HiVal )

{

BracketRange = %LoVal%

}

Else

{

BracketRange = %LoVal% - %HiVal%

}

AffixLevel = %MaxLevel%

Tier := ( ( MaxTier - AffixDataIndex ) + 1 )

If ( Opts . ShowAffixBracketTierTotal )

{

Tier := Tier . "/" . MaxTier

}

}

; Record max possible range regardless of within bounds

If ( Opts . MaxSpanStartingFromFirst )

{

MaxRange = %FRLoVal% - %HiVal%

}

Else

{

MaxRange = %LoVal% - %HiVal%

}

}

}

BracketLevel := AffixLevel

FinalRange := AssembleValueRangeFields ( BracketRange , BracketLevel , MaxRange , MaxLevel )

return FinalRange

}

AssembleValueRangeFields ( BracketRange , BracketLevel , MaxRange = "" , MaxLevel = 0 )

{

Global Opts

If ( Opts . ShowAffixBracket )

{

FinalRange := BracketRange

If ( Opts . ValueRangeFieldWidth > 0 )

{

FinalRange := StrPad ( FinalRange , Opts . ValueRangeFieldWidth , "left" )

}

If ( Opts . ShowAffixLevel )

{

FinalRange := FinalRange . " " . StrPad ( "(" . BracketLevel . ")" , 4 , Side = "left" )

}

Else

{

FinalRange := FinalRange . Opts . AffixDetailDelimiter

}

}

If ( MaxRange and Opts . ShowAffixMaxPossible )

{

If ( Opts . ValueRangeFieldWidth > 0 )

{

MaxRange := StrPad ( MaxRange , Opts . ValueRangeFieldWidth , "left" )

}

FinalRange := FinalRange . MaxRange

If ( Opts . ShowAffixLevel )

{

FinalRange := FinalRange . " " . StrPad ( "(" . MaxLevel . ")" , 4 , Side = "left" )

}

}

return FinalRange

}

ParseRarity ( ItemData_NamePlate )

{

Loop , Parse , ItemData_NamePlate , `n , `r

{

IfInString , A_LoopField , Rarity:

{

StringSplit , RarityParts , A_LoopField , %A_Space%

Break

}

}

return RarityParts %RarityParts% 2

}

Assert ( expr , msg )

{

If ( Not ( expr ) )

{

MsgBox , 4112 , Assertion Failure , %msg%

ExitApp

}

}

GetItemDataChunk ( ItemDataText , MatchWord )

{

Assert ( StrLen ( MatchWord ) > 0 , "GetItemDataChunk: parameter 'MatchWord' can't be empty" )

StringReplace , TempResult , ItemDataText , -------- `r`n , `` , All

StringSplit , ItemDataChunks , TempResult , ``

Loop , %ItemDataChunks0%

{

IfInString , ItemDataChunks %A_Index% , %MatchWord%

{

return ItemDataChunks %A_Index%

}

}

}

ParseQuality ( ItemDataNamePlate )

{

ItemQuality := 0

Loop , Parse , ItemDataNamePlate , `n , `r

{

If ( StrLen ( A_LoopField ) = 0 )

{

Break

}

IfInString , A_LoopField , Unidentified

{

Break

}

IfInString , A_LoopField , Quality:

{

ItemQuality := RegExReplace ( A_LoopField , "Quality: \+(\d+)% .*" , "$1" )

Break

}

}

return ItemQuality

}

ParseAugmentations ( ItemDataChunk , ByRef AffixCSVList )

{

CurAugment := ItemDataChunk

Loop , Parse , ItemDataChunk , `n , `r

{

CurAugment := A_LoopField

Globals . Set ( "CurAugment" , A_LoopField )

IfInString , A_LoopField , Requirements:

{

; too far - Requirements: is already the next chunk

Break

}

IfInString , A_LoopField , ( augmented )

{

StringSplit , LineParts , A_LoopField , :

AffixCSVList := AffixCSVList . "'" . LineParts %LineParts% 1 . "'"

AffixCSVList := AffixCSVList . ", "

}

}

AffixCSVList := SubStr ( AffixCSVList , 1 , - 2 )

}

ParseRequirements ( ItemDataChunk , ByRef Level , ByRef Attributes , ByRef Values = "" )

{

IfNotInString , ItemDataChunk , Requirements

{

return

}

Attr =

AttrValues =

Delim := ","

DelimLen := StrLen ( Delim )

Loop , Parse , ItemDataChunk , `n , `r

{

If StrLen ( A_LoopField ) = 0

{

Break ; Not interested in blank lines

}

IfInString , A_LoopField , Str

{

Attr := Attr . "Str" . Delim

AttrValues := AttrValues . GetColonValue ( A_LoopField ) . Delim

}

IfInString , A_LoopField , Dex

{

Attr := Attr . "Dex" . Delim

AttrValues := AttrValues . GetColonValue ( A_LoopField ) . Delim

}

IfInString , A_LoopField , Int

{

Attr := Attr . "Int" . Delim

AttrValues := AttrValues . GetColonValue ( A_LoopField ) . Delim

}

IfInString , A_LoopField , Level

{

Level := GetColonValue ( A_LoopField )

}

}

; Chop off last Delim

If ( SubStr ( Attr , - ( DelimLen - 1 ) ) == Delim )

{

Attr := SubStr ( Attr , 1 , - ( DelimLen ) )

}

If ( SubStr ( AttrValues , - ( DelimLen - 1 ) ) == Delim )

{

AttrValues := SubStr ( AttrValues , 1 , - ( DelimLen ) )

}

Attributes := Attr

Values := AttrValues

}

; Parses #low-#high and sets Hi to #high and Lo to #low

; if RangeChunk is just a single value (#) it will set both

; Hi and Lo to this single value (effectively making the range 1-1 if # was 1)

ParseRange ( RangeChunk , ByRef Hi , ByRef Lo )

{

IfInString , RangeChunk , -

{

StringSplit , RangeParts , RangeChunk , -

Lo := RegExReplace ( RangeParts1 , "(\d+?)" , "$1" )

Hi := RegExReplace ( RangeParts2 , "(\d+?)" , "$1" )

}

Else

{

Hi := RangeChunk

Lo := RangeChunk

}

}

ParseItemLevel ( ItemDataText )

{

; XXX

; Add support for The Awakening Closed Beta

; Once TA is released we won't need to support both occurences of

; the word "Item level" any more...

ItemDataChunk := GetItemDataChunk ( ItemDataText , "Itemlevel:" )

If ( StrLen ( ItemDataChunk ) <= 0 )

{

ItemDataChunk := GetItemDataChunk ( ItemDataText , "Item Level:" )

}

Assert ( StrLen ( ItemDataChunk ) > 0 , "ParseItemLevel: couldn't parse item data chunk" )

Loop , Parse , ItemDataChunk , `n , `r

{

IfInString , A_LoopField , Itemlevel:

{

StringSplit , ItemLevelParts , A_LoopField , %A_Space%

Result := StrTrimWhitespace ( ItemLevelParts2 )

return Result

}

IfInString , A_LoopField , Item Level:

{

StringSplit , ItemLevelParts , A_LoopField , %A_Space%

Result := StrTrimWhitespace ( ItemLevelParts3 )

return Result

}

}

}

;;hixxie fixed. Shows MapLevel for any map base.

ParseMapLevel ( ItemDataText )

{

ItemDataChunk := GetItemDataChunk ( ItemDataText , "MapTier:" )

If ( StrLen ( ItemDataChunk ) <= 0 )

{

ItemDataChunk := GetItemDataChunk ( ItemDataText , "Map Tier:" )

}

Assert ( StrLen ( ItemDataChunk ) > 0 , "ParseMapLevel: couldn't parse item data chunk" )

Loop , Parse , ItemDataChunk , `n , `r

{

IfInString , A_LoopField , MapTier:

{

StringSplit , MapLevelParts , A_LoopField , %A_Space%

Result := StrTrimWhitespace ( MapLevelParts2 )

return Result

}

IfInString , A_LoopField , Map Tier:

{

StringSplit , MapLevelParts , A_LoopField , %A_Space%

Result := StrTrimWhitespace ( MapLevelParts3 ) + 67

return Result

}

}

}

ParseGemLevel ( ItemDataText , PartialString = "Level:" )

{

ItemDataChunk := GetItemDataChunk ( ItemDataText , PartialString )

Loop , Parse , ItemDataChunk , `n , `r

{

IfInString , A_LoopField , %PartialString%

{

StringSplit , ItemLevelParts , A_LoopField , %A_Space%

Result := StrTrimWhitespace ( ItemLevelParts2 )

return Result

}

}

}

StrMult ( Char , Times )

{

Result =

Loop , %Times%

{

Result := Result . Char

}

return Result

}

StrTrimSpaceLeft ( String )

{

return RegExReplace ( String , " *(.+?)" , "$1" )

}

StrTrimSpaceRight ( String )

{

return RegExReplace ( String , "(.+?) *$" , "$1" )

}

StrTrimSpace ( String )

{

return RegExReplace ( String , " *(.+?) *" , "$1" )

}

StrTrimWhitespace ( String )

{

return RegExReplace ( String , "[ \r

\t]*(.+?)[ \r

\t]*" , "$1" )

}

; Pads a string with a multiple of PadChar to become a wanted total length.

; Note that Side is the side that is padded not the anchored side.

; Meaning, if you pad right side, the text will move left. If Side was an

; anchor instead, the text would move right if anchored right.

StrPad ( String , Length , Side = "right" , PadChar = " " )

{

StringLen , Len , String

AddLen := Length - Len

If ( AddLen <= 0 )

{

return String

}

Pad := StrMult ( PadChar , AddLen )

If ( Side == "right" )

{

Result := String . Pad

}

Else

{

Result := Pad . String

}

return Result

}

; Prefix a string s with another string prefix.

; Does nothing if s is already prefixed.

StrPrefix ( s , prefix ) {

If ( s == "" ) {

return ""

} Else {

If ( SubStr ( s , 1 , StrLen ( prefix ) ) == prefix ) {

return s ; Nothing to do

} Else {

return prefix . s

}

}

}

BoolToString ( flag ) {

If ( flag == True ) {

return "True"

} Else {

return "False"

}

return "False"

}

; Formats a number with SetFormat (leaving A_FormatFloat unchanged)

; Returns formatted Num as string.

NumFormat ( Num , Format )

{

oldFormat := A_FormatFloat

newNum := Num

SetFormat , FloatFast , %Format%

newNum += 0.0 ; convert to float, which applies SetFormat

newNum := newNum . "" ; convert to string so the next SetFormat doesn't apply

SetFormat , FloatFast , %oldFormat%

return newNum

}

; Pads a number with prefixed 0s and optionally rounds or appends to specified decimal places width.

NumPad ( Num , TotalWidth , DecimalPlaces = 0 )

{

myFormat = 0 %TotalWidth% . %DecimalPlaces%

newNum := NumFormat ( Num , myFormat )

return newNum

}

; Estimate indicator, marks end user display values as guesstimated so they can take a look at it.

MarkAsGuesstimate ( ValueRange , Side = "left" , Indicator = " * " )

{

Global Globals , Opts

Globals . Set ( "MarkedAsGuess" , True )

return StrPad ( ValueRange . Indicator , Opts . ValueRangeFieldWidth + StrLen ( Indicator ) , Side )

}

MakeAffixDetailLine ( AffixLine , AffixType , ValueRange , Tier )

{

Global ItemData

Delim := "|" ; Internal delimiter, used as string split char later - do not change to the user adjustable delimiter

Line := AffixLine . Delim . ValueRange . Delim . AffixType

If ( ItemData . Rarity == "Rare" )

{

Line := Line . Delim . Tier

}

return Line

}

AppendAffixInfo ( Line , AffixPos )

{

Global AffixLines

AffixLines . Set ( AffixPos , Line )

}

AssembleAffixDetails ( )

{

Global Opts , AffixLines

AffixLine =

AffixType =

ValueRange =

AffixTier =

NumAffixLines := AffixLines . MaxIndex ( )

AffixLineParts := 0

Loop , %NumAffixLines%

{

CurLine := AffixLines [ A_Index ]

ProcessedLine =

Loop , %AffixLineParts0%

{

AffixLineParts %A_Index% =

}

StringSplit , AffixLineParts , CurLine , |

AffixLine := AffixLineParts1

ValueRange := AffixLineParts2

AffixType := AffixLineParts3

AffixTier := AffixLineParts4

Delim := Opts . AffixDetailDelimiter

Ellipsis := Opts . AffixDetailEllipsis

If ( Opts . ValueRangeFieldWidth > 0 )

{

ValueRange := StrPad ( ValueRange , Opts . ValueRangeFieldWidth , "left" )

}

If ( Opts . MirrorAffixLines == 1 )

{

If ( Opts . MirrorLineFieldWidth > 0 )

{

If ( StrLen ( AffixLine ) > Opts . MirrorLineFieldWidth )

{

AffixLine := StrTrimSpaceRight ( SubStr ( AffixLine , 1 , Opts . MirrorLineFieldWidth ) ) . Ellipsis

}

AffixLine := StrPad ( AffixLine , Opts . MirrorLineFieldWidth + StrLen ( Ellipsis ) )

}

ProcessedLine := AffixLine . Delim

}

IfInString , ValueRange , *

{

ValueRangeString := StrPad ( ValueRange , ( Opts . ValueRangeFieldWidth * 2 ) + ( StrLen ( Opts . AffixDetailDelimiter ) ) )

}

Else

{

ValueRangeString := ValueRange

}

ProcessedLine := ProcessedLine . ValueRangeString . Delim

If ( Opts . ShowAffixBracketTier == 1 and Not ( ItemDataRarity == "Unique" ) and Not StrLen ( AffixTier ) = 0 )

{

If ( InStr ( ValueRange , "*" ) and Opts . ShowAffixBracketTier )

{

TierString := " "

AdditionalPadding := ""

If ( Opts . ShowAffixLevel or Opts . ShowAffixBracketTotalTier )

{

TierString := ""

}

If ( Opts . ShowAffixLevel )

{

AdditionalPadding := AdditionalPadding . StrMult ( " " , Opts . ValueRangeFieldWidth )

}

If ( Opts . ShowAffixBracketTierTotal )

{

AdditionalPadding := AdditionalPadding . StrMult ( " " , Opts . ValueRangeFieldWidth )

}

TierString := TierString . AdditionalPadding

}

Else

{

AddedWidth := 0

If ( Opts . ShowAffixBracketTierTotal )

{

AddedWidth += 2

}

TierString := StrPad ( "T" . AffixTier , 3 + AddedWidth , "left" )

}

ProcessedLine := ProcessedLine . TierString . Delim

}

ProcessedLine := ProcessedLine . AffixType . Delim

Result := Result . "`n" . ProcessedLine

}

return Result

}

AssembleDarkShrineInfo ( )

{

Global Item , ItemData

AffixString := ItemData . Affixes

Found := 0

affixloop:

Loop , Parse , AffixString , `n , `r

{

AffixLine := A_LoopField

If ( AffixLine == "" or AffixLine == "Unidentified" ) {

; ignore empty affixes and unidentified items

continue affixloop

}

Found := Found + 1

DsAffix := ""

If ( RegExMatch ( AffixLine , "[0-9.]+% " ) )

{

DsAffix := RegExReplace ( AffixLine , "[0-9.]+% " , "#% " )

} Else If ( RegExMatch ( AffixLine , "^\+[0-9.]+ " ) ) {

DsAffix := RegExReplace ( AffixLine , "^\+[0-9.]+ " , "+# " )

} Else If ( RegExMatch ( AffixLine , "^\-[0-9.]+ " ) ) {

; Needed for Elreon's mod on jewelry

DsAffix := RegExReplace ( AffixLine , "^\-[0-9.]+ " , "-# " )

} Else If ( RegExMatch ( AffixLine , "^[0-9.]+ " ) ) {

DsAffix := RegExReplace ( AffixLine , "^[0-9.]+ " , "# " )

} Else If ( RegExMatch ( AffixLine , " [0-9]+-[0-9]+ " ) ) {

DsAffix := RegExReplace ( AffixLine , " [0-9]+-[0-9]+ " , " #-# " )

} Else If ( RegExMatch ( AffixLine , "gain [0-9]+ (Power|Frenzy|Endurance) Charge" ) ) {

; Fixes recognition of affixes like "Monsters gain # Endurance Charges every 20 seconds"

DsAffix := RegExReplace ( AffixLine , "gain [0-9]+ " , "gain # " )

} Else If ( RegExMatch ( AffixLine , "fire [0-9]+ additional Projectiles" ) ) {

; Fixes recognition of "Monsters fire # additional Projectiles" affix

DsAffix := RegExReplace ( AffixLine , "[0-9]+" , "#" )

} Else If ( RegExMatch ( AffixLine , "^Reflects [0-9]+" ) ) {

; Fixes recognition of "Reflects # Physical Damage to Melee Attackers" affix

DsAffix := RegExReplace ( AffixLine , "[0-9]+" , "#" )

} Else {

DsAffix := AffixLine

}

Result := Result . "`n " . DsAffix . ":"

; DarkShrineEffects.txt

; File with known effects based on POE wiki and http://poe.rivsoft.net/shrines/shrines.js by https://www.reddit.com/user/d07RiV

Loop , Read , %A_ScriptDir% \data\DarkShrineEffects . txt

{

; This loop retrieves each line from the file, one at a time.

StringSplit , DsEffect , A_LoopReadLine , | ,

if ( DsAffix = DsEffect1 ) {

If ( ( Item . IsRing or Item . IsAmulet or Item . IsBelt or Item . IsJewel ) and ( DsAffix = "+# to Evasion Rating" or DsAffix = "#% Increased Evasion Rating" ) ) {

; Evasion rating on jewelry and jewels has a different effect than Evasion rating on other rares

Result := Result . "`n - Always watch your back (jewelry only)`n -- Three rare monsters spawn around the darkshrine"

} Else If ( ( Item . IsJewel ) and ( DsAffix = "#% increased Critical Strike Chance for Spells" ) ) {

; Crit chance for spells on jewels has a different effect than on other rares

Result := Result . "`n - Keeper of the wand (jewel only)`n -- A rare monster in the area will drop five rare wands"

} Else If ( ( Item . IsJewel ) and ( DsAffix = "#% increased Accuracy Rating" ) ) {

; Accuracy on jewels has a different effect than on other rares

Result := Result . "`n - Shroud your path in the fog of war (jewel only)`n -- Grants permanent Shrouded shrine"

} Else If ( ( Item . IsRing or Item . IsAmulet or Item . IsBelt ) and InStr ( DsAffix , "Adds #-# Chaos Damage" ) ) {

; Flat added chaos damage on jewelry (elreon mod) has a different effect than on weapons (according to wiki)

Result := Result . "`n - Feel the corruption in your veins (jewelry only)`n -- Monsters poison on hit"

} Else {

Result := Result . "`n - " . DsEffect3 . "`n -- " . DsEffect2

}

; TODO: maybe use DsEffect 5 to display warning about complex affixes

; We found the affix so we can continue with the next affix

continue affixloop

}

}

Result := Result . "`n - Unknown"

}

If ( Found <= 2 and not Item . IsUnidentified ) {

; 2 affix rares are consumed

Result := "`n Try again`n Consumes the item, Darkshrine may be used again"

return Result

}

If ( ItemData . Links == 5 ) {

Result := Result . "`n 5-Linked:`n - You win some and you lose some`n -- Randomizes the numerical values of explicit mods on a random item"

}

If ( Item . IsCorrupted ) {

Result := Result . "`n Corrupted:`n - The influence of vaal continues long after their civilization has crumbled`n -- Opens portals to a corrupted area"

}

If ( Item . Quality == 20 ) {

Result := Result . "`n 20% Quality:`n - Wait, what was that sound?`n -- Random item gets a skin transfer"

}

If ( Item . IsMirrored ) {

Result := Result . "`n Mirrored:`n - The little things add up`n -- Possibly rerolls an implicit mod on one item"

}

If ( Item . IsUnidentified ) {

Result := Result . "`n Unidentified:`n - Same effect as if the item is identified first"

}

return Result

}

; Same as AdjustRangeForQuality, except that Value is just

; a single value and not a range.

AdjustValueForQuality ( Value , ItemQuality , Direction = "up" )

{

If ( ItemQuality < 1 )

return Value

Divisor := ItemQuality / 100

If ( Direction == "up" )

{

Result := Round ( Value + ( Value * Divisor ) )

}

Else

{

Result := Round ( Value - ( Value * Divisor ) )

}

return Result

}

; Adjust an affix' range for +% Quality on an item.

; For example: given the range 10-20 and item quality +15%

; the result would be 11.5-23 which is currently rounded up

; to 12-23. Note that Direction does not play a part in rounding

; rather it controls if adjusting up towards quality increase or

; down from quality increase (to get the original value back)

AdjustRangeForQuality ( ValueRange , ItemQuality , Direction = "up" )

{

If ( ItemQuality = 0 )

{

return ValueRange

}

VRHi := 0

VRLo := 0

ParseRange ( ValueRange , VRHi , VRLo )

Divisor := ItemQuality / 100

If ( Direction == "up" )

{

VRHi := Round ( VRHi + ( VRHi * Divisor ) )

VRLo := Round ( VRLo + ( VRLo * Divisor ) )

}

Else

{

VRHi := Round ( VRHi - ( VRHi * Divisor ) )

VRLo := Round ( VRLo - ( VRLo * Divisor ) )

}

If ( VRLo == VRHi )

{

ValueRange = %VRLo%

}

Else

{

ValueRange = %VRLo% - %VRHi%

}

return ValueRange

}

; Checks ActualValue against ValueRange, returning 1 if

; ActualValue is within bounds of ValueRange, 0 otherwise.

WithinBounds ( ValueRange , ActualValue )

{

VHi := 0

VLo := 0

ParseRange ( ValueRange , VHi , VLo )

Result := 1

IfInString , ActualValue , -

{

AVHi := 0

AVLo := 0

ParseRange ( ActualValue , AVHi , AVLo )

If ( ( AVLo < VLo ) or ( AVHi > VHi ) )

{

Result := 0

}

}

Else

{

If ( ( ActualValue < VLo ) or ( ActualValue > VHi ) )

{

Result := 0

}

}

return Result

}

GetAffixTypeFromProcessedLine ( PartialAffixString )

{

Global AffixLines

NumAffixLines := AffixLines . MaxIndex ( )

Loop , %NumAffixLines%

{

AffixLine := AffixLines [ A_Index ]

IfInString , AffixLine , %PartialAffixString%

{

StringSplit , AffixLineParts , AffixLine , |

return AffixLineParts3

}

}

}

; Get actual value from a line of the ingame tooltip as a number

; that can be used in calculations.

GetActualValue ( ActualValueLine )

{

Result := RegExReplace ( ActualValueLine , ".*?\+?(\d+(?:-\d+|\.\d+)?).*" , "$1" )

return Result

}

; Get value from a colon line, e.g. given the line "Level: 57", returns the number 57

GetColonValue ( Line )

{

IfInString , Line , :

{

StringSplit , LineParts , Line , :

Result := StrTrimSpace ( LineParts %LineParts% 2 )

return Result

}

}

RangeMid ( Range )

{

If ( Range = 0 or Range = "0" or Range = "0-0" )

{

return 0

}

RHi := 0

RLo := 0

ParseRange ( Range , RHi , RLo )

RSum := RHi + RLo

If ( RSum == 0 )

{

return 0

}

return Floor ( ( RHi + RLo ) / 2 )

}

RangeMin ( Range )

{

If ( Range = 0 or Range = "0" or Range = "0-0" )

{

return 0

}

RHi := 0

RLo := 0

ParseRange ( Range , RHi , RLo )

return RLo

}

RangeMax ( Range )

{

If ( Range = 0 or Range = "0" or Range = "0-0" )

{

return 0

}

RHi := 0

RLo := 0

ParseRange ( Range , RHi , RLo )

return RHi

}

AddRange ( Range1 , Range2 )

{

R1Hi := 0

R1Lo := 0

R2Hi := 0

R2Lo := 0

ParseRange ( Range1 , R1Hi , R1Lo )

ParseRange ( Range2 , R2Hi , R2Lo )

FinalHi := R1Hi + R2Hi

FinalLo := R1Lo + R2Lo

FinalRange = %FinalLo% - %FinalHi%

return FinalRange

}

; Used to check return values from LookupAffixBracket()

IsValidBracket ( Bracket )

{

If ( Bracket == "n/a" )

{

return False

}

return True

}

; Used to check return values from LookupAffixData()

IsValidRange ( Bracket )

{

IfInString , Bracket , n / a

{

return False

}

return True

}

; Note that while ExtractCompAffixBalance() can be run on processed data

; that has compact affix type declarations (or not) for this function to

; work properly, make sure to run it on data that has compact affix types

; turned off. The reason being that it is hard to count prefixes by there

; being a "P" in a line that also has mirrored affix descriptions.

ExtractTotalAffixBalance ( ProcessedData , ByRef Prefixes , ByRef Suffixes , ByRef CompPrefixes , ByRef CompSuffixes )

{

Loop , Parse , ProcessedData , `n , `r

{

AffixLine := A_LoopField

IfInString , AffixLine , Comp . Prefix

{

CompPrefixes += 1

}

IfInString , AffixLine , Comp . Suffix

{

CompSuffixes += 1

}

}

ProcessedData := RegExReplace ( ProcessedData , "Comp\. Prefix" , "" )

ProcessedData := RegExReplace ( ProcessedData , "Comp\. Suffix" , "" )

Loop , Parse , ProcessedData , `n , `r

{

AffixLine := A_LoopField

IfInString , AffixLine , Prefix

{

Prefixes += 1

}

IfInString , AffixLine , Suffix

{

Suffixes += 1

}

}

}

ExtractCompositeAffixBalance ( ProcessedData , ByRef CompPrefixes , ByRef CompSuffixes )

{

Loop , Parse , ProcessedData , `n , `r

{

AffixLine := A_LoopField

IfInString , AffixLine , Comp . Prefix

{

CompPrefixes += 1

}

IfInString , AffixLine , Comp . Suffix

{

CompSuffixes += 1

}

}

}

ParseFlaskAffixes ( ItemDataAffixes )

{

Global AffixTotals

IfInString , ItemDataChunk , Unidentified

{

return ; Not interested in unidentified items

}

NumPrefixes := 0

NumSuffixes := 0

Loop , Parse , ItemDataAffixes , `n , `r

{

If StrLen ( A_LoopField ) = 0

{

Continue ; Not interested in blank lines

}

; Suffixes

IfInString , A_LoopField , Dispels

{

; Covers Shock, Burning and Frozen and Chilled

If ( NumSuffixes < 1 )

{

NumSuffixes += 1

}

Continue

}

IfInString , A_LoopField , Removes Bleeding

{

If ( NumSuffixes < 1 )

{

NumSuffixes += 1

}

Continue

}

IfInString , A_LoopField , Removes Curses on use

{

If ( NumSuffixes < 1 )

{

NumSuffixes += 1

}

Continue

}

IfInString , A_LoopField , during flask effect

{

If ( NumSuffixes < 1 )

{

NumSuffixes += 1

}

Continue

}

IfInString , A_LoopField , Adds Knockback

{

If ( NumSuffixes < 1 )

{

NumSuffixes += 1

}

Continue

}

IfInString , A_LoopField , Life Recovery to Minions

{

If ( NumSuffixes < 1 )

{

NumSuffixes += 1

}

Continue

}

; Prefixes

IfInString , A_LoopField , Recovery Speed

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

IfInString , A_LoopField , Amount Recovered

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

IfInString , A_LoopField , Charges

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

IfInString , A_LoopField , Instant

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

IfInString , A_LoopField , Charge when

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

IfInString , A_LoopField , Recovery when

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

IfInString , A_LoopField , Mana Recovered

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

IfInString , A_LoopField , Life Recovered

{

If ( NumPrefixes < 1 )

{

NumPrefixes += 1

}

Continue

}

}

AffixTotals . NumPrefixes := NumPrefixes

AffixTotals . NumSuffixes := NumSuffixes

}

; Try looking up the remainder bracket based on Bracket

; This is done by calculating the rest value in three

; different ways, falling through if not successful:

;

; 1) CurrValue - RangeMid(Bracket)

; 2) CurrValue - RangeMin(Bracket)

; 3) CurrValue - RangeMax(Bracket)

;

; (Internal: RegExr x-forms):

;

; with ByRef BracketLevel:

; ( *)(.+Rest) := CurrValue - RangeMid\((.+)\)\r *(.+) := LookupAffixBracket\((.+?), (.+?), (.+?), (.+?)\)

; -> $1$4 := LookupRemainingAffixBracket($5, $6, CurrValue, $3, $8)

;

; w/o ByRef BracketLevel:

; ( *)(.+Rest) := CurrValue - RangeMid\((.+)\)\r *(.+) := LookupAffixBracket\((.+?), (.+?), (.+?)\)

; -> $1$4 := LookupRemainingAffixBracket($5, $6, CurrValue, $3)

;

LookupRemainingAffixBracket ( Filename , ItemLevel , CurrValue , Bracket , ByRef BracketLevel = 0 )

{

RestValue := CurrValue - RangeMid ( Bracket )

RemainderBracket := LookupAffixBracket ( Filename , ItemLevel , RestValue , BracketLevel )

If ( Not IsValidBracket ( RemainderBracket ) )

{

RestValue := CurrValue - RangeMin ( Bracket )

RemainderBracket := LookupAffixBracket ( Filename , ItemLevel , RestValue , BracketLevel )

}

If ( Not IsValidBracket ( RemainderBracket ) )

{

RestValue := CurrValue - RangeMax ( Bracket )

RemainderBracket := LookupAffixBracket ( Filename , ItemLevel , RestValue , BracketLevel )

}

return RemainderBracket

}

ParseAffixes ( ItemDataAffixes , Item )

{

Global Globals , Opts , AffixTotals

ItemDataChunk := ItemDataAffixes

ItemBaseType := Item . BaseType

ItemSubType := Item . SubType

ItemGripType := Item . GripType

ItemLevel := Item . Level

ItemQuality := Item . Quality

; Reset the AffixLines "array" and other vars

ResetAffixDetailVars ( )

; Keeps track of how many affix lines we have so they can be assembled later.

; Acts as a loop index variable when iterating each affix data part.

NumPrefixes := 0

NumSuffixes := 0

; Composition flags

;

; These are required for descision making later, when guesstimating

; sources for parts of a value from composite and/or same name affixes.

; They will be set to the line number where they occur in the pre-pass

; loop, so that details for that line can be changed later after we

; have more clues for possible compositions.

HasIIQ := 0

HasIncrArmour := 0

HasIncrEvasion := 0

HasIncrEnergyShield := 0

HasHybridDefences := 0

HasIncrArmourAndES := 0

HasIncrArmourAndEvasion := 0

HasIncrEvasionAndES := 0

HasIncrLightRadius := 0

HasIncrAccuracyRating := 0

HasIncrPhysDmg := 0

HasToAccuracyRating := 0

HasStunRecovery := 0

HasSpellDamage := 0

HasMaxMana := 0

HasMultipleCrafted := 0

; Max mana already accounted for in case of Composite Prefix+Prefix

; "Spell Damage / Max Mana" + "Max Mana"

MaxManaPartial =

; Accuracy Rating already accounted for in case of

; Composite Prefix + Composite Suffix:

; "increased Physical Damage / to Accuracy Rating" +

; "to Accuracy Rating / Light Radius"

; Composite Prefix + Suffix:

; "increased Physical Damage / to Accuracy Rating" +

; "to Accuracy Rating"

ARPartial =

ARAffixTypePartial =

; Partial for the former "Block and Stun Recovery"

; Note: with PoE v1.3+ now called just "increased Stun Recovery"

BSRecPartial =

; --- PRE-PASS ---

; To determine composition flags

Loop , Parse , ItemDataChunk , `n , `r

{

If StrLen ( A_LoopField ) = 0

{

Break ; Not interested in blank lines

}

IfInString , ItemDataChunk , Unidentified

{

Break ; Not interested in unidentified items

}

IfInString , A_LoopField , increased Light Radius

{

HasIncrLightRadius := A_Index

Continue

}

IfInString , A_LoopField , increased Quantity

{

HasIIQ := A_Index

Continue

}

IfInString , A_LoopField , increased Physical Damage

{

HasIncrPhysDmg := A_Index

Continue

}

IfInString , A_LoopField , increased Accuracy Rating

{

HasIncrAccuracyRating := A_Index

Continue

}

IfInString , A_LoopField , to Accuracy Rating

{

HasToAccuracyRating := A_Index

Continue

}

IfInString , A_LoopField , increased Armour and Evasion

{

HasHybridDefences := A_Index

HasIncrArmourAndEvasion := A_Index

Continue

}

IfInString , A_LoopField , increased Armour and Energy Shield

{

HasHybridDefences := A_Index

HasIncrArmourAndES := A_Index

Continue

}

IfInString , A_LoopField , increased Evasion and Energy Shield

{

HasHybridDefences := A_Index

HasIncrEvasionAndES := A_Index

Continue

}

IfInString , A_LoopField , increased Armour

{

HasIncrArmour := A_Index

Continue

}

IfInString , A_LoopField , increased Evasion Rating

{

HasIncrEvasion := A_Index

Continue

}

IfInString , A_LoopField , increased Energy Shield

{

HasIncrEnergyShield := A_Index

Continue

}

IfInString , A_LoopField , increased Stun Recovery

{

HasStunRecovery := A_Index

Continue

}

IfInString , A_LoopField , increased Spell Damage

{

HasSpellDamage := A_Index

Continue

}

IfInString , A_LoopField , to maximum Mana

{

HasMaxMana := A_Index

Continue

}

IfInString , A_Loopfield , Can have multiple Crafted Mods

{

HasMultipleCrafted := A_Index

Continue

}

}

; Note: yes, these superlong IfInString structures suck, but hey,

; AHK sucks as an object-oriented scripting language, so bite me.

;

; But in all seriousness, there are two main parts - Simple and

; Complex Affixes - which could be refactored into their own helper

; methods.

; --- SIMPLE AFFIXES ---

Loop , Parse , ItemDataChunk , `n , `r

{

If StrLen ( A_LoopField ) = 0

{

Break ; Not interested in blank lines

}

IfInString , ItemDataChunk , Unidentified

{

Break ; Not interested in unidentified items

}

CurrValue := GetActualValue ( A_LoopField )

CurrTier := 0

BracketLevel := 0

; Suffixes

IfInString , A_LoopField , increased Attack Speed

{

; Slinkston edit. Cleaned up the code. I think this is a better approach.

NumSuffixes += 1

If ( ItemSubType == "Wand" or ItemSubType == "Bow" )

{

ValueRange := LookupAffixData ( "data\AttackSpeed_BowsAndWands.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemBaseType == "Weapon" )

{

ValueRange := LookupAffixData ( "data\AttackSpeed_Weapons.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\AttackSpeed_ArmourAndItems.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Accuracy Rating

{

AffixType := "Comp. Suffix"

ValueRange := LookupAffixData ( "data\IncrAccuracyRating_LightRadius.txt" , ItemLevel , CurrValue , "" , CurrTier )

NumSuffixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , AffixType , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to all Attributes

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ToAllAttributes.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Strength

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ToStrength.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Intelligence

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ToIntelligence.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Dexterity

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ToDexterity.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Cast Speed

{

; Slinkston edit

If ( ItemBaseType == "Weapon" )

{

ValueRange := LookupAffixData ( "data\CastSpeedWeapon.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\CastSpeed.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

NumSuffixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

; This needs to come before "Critical Strike Chance" !

IfInString , A_LoopField , increased Critical Strike Chance for Spells

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\SpellCritChance.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Critical Strike Chance

{

; Slinkston edit

If ( ItemBaseType == "Weapon" )

{

ValueRange := LookupAffixData ( "data\CritChanceLocal.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\CritChanceGlobal.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

NumSuffixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Critical Strike Multiplier

{

; Slinkston edit

If ( ItemBaseType == "Weapon" )

{

ValueRange := LookupAffixData ( "data\CritMultiplierLocal.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\CritMultiplierGlobal.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

NumSuffixes += 1

Continue

}

IfInString , A_LoopField , increased Fire Damage

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\IncrFireDamage.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Cold Damage

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\IncrColdDamage.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Lightning Damage

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\IncrLightningDamage.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Light Radius

{

ValueRange := LookupAffixData ( "data\LightRadius_AccuracyRating.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Comp. Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Chance to Block

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\BlockChance.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

; Flask affixes (on belts)

IfInString , A_LoopField , reduced Flask Charges used

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\FlaskChargesUsed.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Flask Charges gained

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\FlaskChargesGained.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Flask effect duration

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\FlaskDuration.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Quantity

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\IIQ.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Life gained on Kill

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\LifeOnKill.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Life gained for each Enemy hit ; Cuts off the rest to accommodate both "by Attacks" and "by your Attacks"

{

; Slinkston edit. This isn't necessary at this point in time, but if either were to gain an additional ilvl affix down the road this would already be in place

If ( ItemBaseType == "Weapon" )

{

ValueRange := LookupAffixData ( "data\LifeOnHitLocal.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\LifeOnHit.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

NumSuffixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Life Regenerated per second

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\LifeRegen.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Mana Gained on Kill

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ManaOnKill.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Mana Regeneration Rate

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ManaRegen.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Projectile Speed

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ProjectileSpeed.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , reduced Attribute Requirements

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ReducedAttrReqs.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to all Elemental Resistances

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\AllResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Fire Resistance

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\FireResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Lightning Resistance

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\LightningResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Cold Resistance

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ColdResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Chaos Resistance

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ChaosResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

If RegExMatch ( A_LoopField , ".*to (Cold|Fire|Lightning) and (Cold|Fire|Lightning) Resistances" )

{

; Catches two-stone rings and the like which have "+#% to Cold and Lightning Resistances"

IfInString , A_LoopField , Fire

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\FireResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Lightning

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\LightningResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , Cold

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\ColdResist.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

}

IfInString , A_LoopField , increased Stun Duration on Enemies

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\StunDuration.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , reduced Enemy Stun Threshold

{

NumSuffixes += 1

ValueRange := LookupAffixData ( "data\StunThreshold.txt" , ItemLevel , CurrValue , "" , CurrTier )

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Suffix" , ValueRange , CurrTier ) , A_Index )

Continue

}

; Prefixes

IfInString , A_LoopField , to Armour

{

; Slinkston edit. AR, EV, and ES items are all correct for Armour, Shields, Helmets, Boots, Gloves, and different jewelry.

; to Armour has Belt, but does not have Ring or Amulet.

If ( ItemSubType == "Belt" )

{

ValueRange := LookupAffixData ( "data\ToArmourBelt.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemSubtype == "Helmet" )

{

ValueRange := LookupAffixData ( "data\ToArmourHelmet.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemSubtype == "Gloves" or ItemSubType == "Boots" )

{

ValueRange := LookupAffixData ( "data\ToArmourGlovesandBoots.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\ToArmourArmourandShield.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Prefix" , ValueRange , CurrTier ) , A_Index )

NumPrefixes += 1

Continue

}

IfInString , A_LoopField , increased Armour and Evasion

{

AffixType := "Prefix"

AEBracketLevel := 0

ValueRange := LookupAffixData ( "data\ArmourAndEvasion.txt" , ItemLevel , CurrValue , AEBracketLevel , CurrTier )

If ( HasStunRecovery )

{

AEBracketLevel2 := AEBracketLevel

AEBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue , AEBracketLevel2 )

If ( Not IsValidRange ( ValueRange ) and IsValidBracket ( AEBracket ) )

{

ValueRange := LookupAffixData ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue , AEBracketLevel2 , CurrTier )

}

AffixType := "Comp. Prefix"

BSRecBracketLevel := 0

BSRecValue := ExtractValueFromAffixLine ( ItemDataChunk , "increased Stun Recovery" )

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , AEBracketLevel2 , "" , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) or Not IsValidBracket ( AEBracket ) )

{

; This means that we are actually dealing with a Prefix + Comp. Prefix.

; To get the part for the hybrid defence that is contributed by the straight prefix,

; lookup the bracket level for the B&S Recovery line and then work out the partials

; for the hybrid stat from the bracket level of B&S Recovery.

;

; For example:

; 87% increased Armour and Evasion

; 7% increased Stun Recovery

;

; 1) 7% B&S indicates bracket level 2 (6-7)

; 2) Lookup bracket level 2 from the hybrid stat + block and stun recovery table

; This works out to be 6-14.

; 3) Subtract 6-14 from 87 to get the rest contributed by the hybrid stat as pure prefix.

;

; Currently when subtracting a range from a single value we just use the range's

; max as single value. This may need changing depending on circumstance but it

; works for now. EDIT: no longer the case, now uses RangeMid(...). EDIT2: Rest value calc

; now routed through LookupRemainingAffixBracket() which uses trickle-down through all

; three Range... functions. #'s below NOT YET changed to reflect that...

; 87-10 = 77

; 4) lookup affix data for increased Armour and Evasion with value of 77

;

; We now know, this is a Comp. Prefix+Prefix

;

BSRecBracketLevel := 0

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , ItemLevel , BSRecValue , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) )

{

; This means that the hybrid stat is a Comp. Prefix (Hybrid)+Prefix and SR is a Comp. Prefix (Hybrid)+Suffix.

;

; For example the following case:

; Item Level: 58

; 107% increased Armour and Evasion (AE)

; ...

; 30% increased Stun Recovery (SR)

;

; Based on item level, 33-41 is the max contribution for AE of HybridDefences_StunRecovery (Comp. Prefix),

; 12-13 is the max contribution for Stun Rec of StunRecovery_Hybrid (Comp. Prefix), 23-25 is the max contribution

; for SR of StunRecovery_Suffix (Suffix)

;

; Obviously this is ambiguous and tough to resolve, but we'll try anyway...

;

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , ItemLevel , "" , BSRecBracketLevel )

}

AEBSBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , BSRecBracketLevel )

If ( Not WithinBounds ( AEBSBracket , CurrValue ) )

{

AEBracket := LookupRemainingAffixBracket ( "data\ArmourAndEvasion.txt" , ItemLevel , CurrValue , AEBSBracket )

If ( Not IsValidBracket ( AEBracket ) )

{

AEBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue )

}

If ( IsValidBracket ( AEBracket ) and WithinBounds ( AEBracket , CurrValue ) )

{

If ( NumPrefixes < 2 )

{

ValueRange := AddRange ( AEBSBracket , AEBracket )

ValueRange := MarkAsGuesstimate ( ValueRange )

AffixType := "Comp. Prefix+Prefix"

NumPrefixes += 1

}

Else

{

ValueRange := LookupAffixData ( "data\ArmourAndEvasion.txt" , ItemLevel , CurrValue , AEBracketLevel2 , CurrTier )

AffixType := "Prefix"

}

}

Else

{

; Check if it isn't a simple case of Armour and Evasion (Prefix) + Stun Recovery (Suffix)

BSRecBracket := LookupAffixBracket ( "data\StunRecovery_Suffix.txt" , ItemLevel , BSRecValue , BSRecBracketLevel , CurrTier )

If ( IsValidRange ( ValueRange ) and IsValidBracket ( BSRecBracket ) )

{

; -2 means for later that processing this hybrid defence stat

; determined that Stun Recovery should be a simple suffix

BSRecPartial := ""

AffixType := "Prefix"

ValueRange := LookupAffixData ( "data\ArmourAndEvasion.txt" , ItemLevel , CurrValue , AEBracketLevel , CurrTier )

}

}

}

If ( WithinBounds ( BSRecPartial , BSRecValue ) )

{

; BS Recovery value within bounds, this means BS Rec is all acounted for

BSRecPartial =

}

}

}

NumPrefixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , AffixType , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Armour and Energy Shield

{

AffixType := "Prefix"

AESBracketLevel := 0

ValueRange := LookupAffixData ( "data\ArmourAndEnergyShield.txt" , ItemLevel , CurrValue , AESBracketLevel , CurrTier )

If ( HasStunRecovery )

{

AESBracketLevel2 := AESBracketLevel

AESBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue , AESBracketLevel2 )

If ( Not IsValidRange ( ValueRange ) and IsValidBracket ( AESBracket ) )

{

ValueRange := LookupAffixData ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue , AESBracketLevel2 , CurrTier )

}

AffixType := "Comp. Prefix"

BSRecBracketLevel := 0

BSRecValue := ExtractValueFromAffixLine ( ItemDataChunk , "increased Stun Recovery" )

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , AESBracketLevel2 , "" , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) or Not IsValidBracket ( AESBracket ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Armour.txt" , AESBracketLevel , "" , BSRecBracketLevel )

}

If ( Not IsValidBracket ( BSRecPartial ) )

{

BSRecBracketLevel := 0

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , ItemLevel , BSRecValue , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , ItemLevel , "" , BSRecBracketLevel )

}

AESBSBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , BSRecBracketLevel )

If ( Not WithinBounds ( AESBSBracket , CurrValue ) )

{

AESBracket := LookupRemainingAffixBracket ( "data\ArmourAndEnergyShield.txt" , ItemLevel , CurrValue , AESBSBracket )

If ( Not IsValidBracket ( AESBracket ) )

{

AESBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue )

}

If ( Not WithinBounds ( AESBracket , CurrValue ) )

{

ValueRange := AddRange ( AESBSBracket , AESBracket )

ValueRange := MarkAsGuesstimate ( ValueRange )

AffixType := "Comp. Prefix+Prefix"

NumPrefixes += 1

}

}

If ( WithinBounds ( BSRecPartial , BSRecValue ) )

{

; BS Recovery value within bounds, this means BS Rec is all acounted for

BSRecPartial =

}

}

}

NumPrefixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , AffixType , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Evasion and Energy Shield

{

AffixType := "Prefix"

EESBracketLevel := 0

ValueRange := LookupAffixData ( "data\EvasionAndEnergyShield.txt" , ItemLevel , CurrValue , EESBracketLevel , CurrTier )

If ( HasStunRecovery )

{

EESBracketLevel2 := EESBracketLevel

EESBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue , EESBracketLevel2 )

If ( Not IsValidRange ( ValueRange ) and IsValidBracket ( EESBracket ) )

{

ValueRange := LookupAffixData ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue , EESBracketLevel2 , CurrTier )

}

AffixType := "Comp. Prefix"

BSRecBracketLevel := 0

BSRecValue := ExtractValueFromAffixLine ( ItemDataChunk , "increased Stun Recovery" )

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , EESBracketLevel2 , "" , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) or Not IsValidBracket ( EESBracket ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , EESBracketLevel , "" , BSRecBracketLevel )

}

If ( Not IsValidBracket ( BSRecPartial ) )

{

BSRecBracketLevel := 0

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , ItemLevel , BSRecValue , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Hybrid.txt" , ItemLevel , "" , BSRecBracketLevel )

}

EESBSBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , BSRecBracketLevel )

If ( Not WithinBounds ( EESBSBracket , CurrValue ) )

{

EESBracket := LookupRemainingAffixBracket ( "data\EvasionAndEnergyShield.txt" , ItemLevel , CurrValue , EESBSBracket )

If ( Not IsValidBracket ( EESBracket ) )

{

EESBracket := LookupAffixBracket ( "data\HybridDefences_StunRecovery.txt" , ItemLevel , CurrValue )

}

If ( Not WithinBounds ( EESBracket , CurrValue ) )

{

ValueRange := AddRange ( EESBSBracket , EESBracket )

ValueRange := MarkAsGuesstimate ( ValueRange )

AffixType := "Comp. Prefix+Prefix"

NumPrefixes += 1

}

}

If ( WithinBounds ( BSRecPartial , BSRecValue ) )

{

; BS Recovery value within bounds, this means BS Rec is all acounted for

BSRecPartial =

}

}

}

NumPrefixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , AffixType , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Armour

{

AffixType := "Prefix"

IABracketLevel := 0

If ( ItemBaseType == "Item" )

{

; Global

PrefixPath := "data\IncrArmour_Items.txt"

PrefixPathOther := "data\IncrArmour_WeaponsAndArmour.txt"

}

Else

{

; Local

PrefixPath := "data\IncrArmour_WeaponsAndArmour.txt"

PrefixPathOther := "data\IncrArmour_Items.txt"

}

ValueRange := LookupAffixData ( PrefixPath , ItemLevel , CurrValue , IABracketLevel , CurrTier )

If ( Not IsValidRange ( ValueRange ) )

{

ValueRange := LookupAffixData ( PrefixPathOther , ItemLevel , CurrValue , IABracketLevel , CurrTier )

}

If ( HasStunRecovery )

{

IABracketLevel2 := IABracketLevel

ASRBracket := LookupAffixBracket ( "data\Armour_StunRecovery.txt" , ItemLevel , CurrValue , IABracketLevel2 )

If ( Not IsValidRange ( ValueRange ) and IsValidBracket ( ASRBracket ) )

{

ValueRange := LookupAffixData ( "data\Armour_StunRecovery.txt" , ItemLevel , CurrValue , IABracketLevel2 , CurrTier )

}

AffixType := "Comp. Prefix"

BSRecBracketLevel := 0

BSRecValue := ExtractValueFromAffixLine ( ItemDataChunk , "increased Stun Recovery" )

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Armour.txt" , IABracketLevel2 , "" , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) or Not IsValidBracket ( ASRBracket ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Armour.txt" , IABracketLevel , "" , BSRecBracketLevel )

}

If ( Not IsValidBracket ( BSRecPartial ) )

{

BSRecBracketLevel := 0

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Armour.txt" , ItemLevel , BSRecValue , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Armour.txt" , ItemLevel , "" , BSRecBracketLevel )

}

IABSBracket := LookupAffixBracket ( "data\Armour_StunRecovery.txt" , BSRecBracketLevel )

If ( Not WithinBounds ( IABSBracket , CurrValue ) )

{

IABracket := LookupRemainingAffixBracket ( PrefixPath , ItemLevel , CurrValue , IABSBracket )

If ( Not IsValidBracket ( IABracket ) )

{

IABracket := LookupAffixBracket ( PrefixPath , ItemLevel , CurrValue )

}

If ( Not WithinBounds ( IABracket , CurrValue ) )

{

ValueRange := AddRange ( IABSBracket , IABracket )

ValueRange := MarkAsGuesstimate ( ValueRange )

AffixType := "Comp. Prefix+Prefix"

NumPrefixes += 1

}

}

If ( WithinBounds ( BSRecPartial , BSRecValue ) )

{

; BS Recovery value within bounds, this means BS Rec is all acounted for

BSRecPartial =

}

}

}

NumPrefixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , AffixType , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to Evasion Rating

{

; Slinkston edit. I am not sure if using 'else if' statements are the best way here, but it seems to work.

; AR, EV, and ES items are all correct for Armour, Shields, Helmets, Boots, Gloves, and different jewelry.

; to Evasion Rating has Ring, but does not have Belt or Amulet.

If ( ItemSubType == "Ring" )

{

ValueRange := LookupAffixData ( "data\ToEvasionRing.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemSubType == "Helmet" )

{

ValueRange := LookupAffixData ( "data\ToEvasionHelmet.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemSubType == "Gloves" or ItemSubType == "Boots" )

{

ValueRange := LookupAffixData ( "data\ToEvasionGlovesandBoots.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\ToEvasionArmourandShield.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

NumPrefixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Prefix" , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , increased Evasion Rating

{

AffixType := "Prefix"

IEBracketLevel := 0

If ( ItemBaseType == "Item" )

{

; Global

PrefixPath := "data\IncrEvasion_Items.txt"

PrefixPathOther := "data\IncrEvasion_Armour.txt"

}

Else

{

; Local

PrefixPath := "data\IncrEvasion_Armour.txt"

PrefixPathOther := "data\IncrEvasion_Items.txt"

}

ValueRange := LookupAffixData ( PrefixPath , ItemLevel , CurrValue , IEBracketLevel , CurrTier )

If ( Not IsValidRange ( ValueRange ) )

{

ValueRange := LookupAffixData ( PrefixPathOther , ItemLevel , CurrValue , IEBracketLevel , CurrTier )

}

If ( HasStunRecovery )

{

IEBracketLevel2 := IEBracketLevel

; Determine composite bracket level and store in IEBracketLevel2, for example:

; 8% increased Evasion

; 26% increased Stun Recovery

; => 8% is bracket level 2 (6-14), so 'B&S Recovery from Evasion' level 2 makes

; BSRec partial 6-7

ERSRBracket := LookupAffixBracket ( "data\Evasion_StunRecovery.txt" , ItemLevel , CurrValue , IEBracketLevel2 )

If ( Not IsValidRange ( ValueRange ) and IsValidBracket ( ERSRBracket ) )

{

ValueRange := LookupAffixData ( "data\Evasion_StunRecovery.txt" , ItemLevel , CurrValue , IEBracketLevel2 , CurrTier )

}

AffixType := "Comp. Prefix"

BSRecBracketLevel := 0

BSRecValue := ExtractValueFromAffixLine ( ItemDataChunk , "increased Stun Recovery" )

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Evasion.txt" , IEBracketLevel2 , "" , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) or Not IsValidBracket ( ERSRBracket ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Evasion.txt" , IEBracketLevel , "" , BSRecBracketLevel )

}

If ( Not IsValidRange ( ValueRange ) and ( Not IsValidBracket ( BSRecPartial ) or Not WithinBounds ( BSRecPartial , BSRecValue ) ) )

{

BSRecBracketLevel := 0

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Evasion.txt" , ItemLevel , BSRecValue , BSRecBracketLevel )

If ( Not IsValidBracket ( BSRecPartial ) )

{

BSRecPartial := LookupAffixBracket ( "data\StunRecovery_Evasion.txt" , ItemLevel , "" , BSRecBracketLevel )

}

IEBSBracket := LookupAffixBracket ( "data\Evasion_StunRecovery.txt" , BSRecBracketLevel )

If ( Not WithinBounds ( IEBSBracket , CurrValue ) )

{

IEBracket := LookupRemainingAffixBracket ( PrefixPath , ItemLevel , CurrValue , IEBSBracket )

If ( Not IsValidBracket ( IEBracket ) )

{

IEBracket := LookupAffixBracket ( PrefixPath , ItemLevel , CurrValue , "" )

}

If ( Not WithinBounds ( IEBracket , CurrValue ) )

{

ValueRange := AddRange ( IEBSBracket , IEBracket )

ValueRange := MarkAsGuesstimate ( ValueRange )

AffixType := "Comp. Prefix+Prefix"

NumPrefixes += 1

}

}

If ( WithinBounds ( BSRecPartial , BSRecValue ) )

{

; BS Recovery value within bounds, this means BS Rec is all acounted for

BSRecPartial =

}

}

}

NumPrefixes += 1

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , AffixType , ValueRange , CurrTier ) , A_Index )

Continue

}

IfInString , A_LoopField , to maximum Energy Shield

{

; Slinkston Edit. Seems I may have to do the same for EV and AR.

; AR, EV, and ES items are all correct for Armour, Shields, Helmets, Boots, Gloves, and different jewelry.

; to max ES is found is all jewelry; Amulet, Belt, and Ring.

PrefixType := "Prefix"

If ( ItemSubType == "Amulet" or ItemSubType == "Belt" )

{

ValueRange := LookupAffixData ( "data\ToMaxESAmuletandBelt.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemSubType == "Ring" )

{

ValueRange := LookupAffixData ( "data\ToMaxESRing.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemSubType == "Gloves" or ItemSubtype == "Boots" )

{

ValueRange := LookupAffixData ( "data\ToMaxESGlovesandBoots.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else If ( ItemSubType == "Helmet" )

{

ValueRange := LookupAffixData ( "data\ToMaxESHelmet.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

Else

{

ValueRange := LookupAffixData ( "data\ToMaxESArmourandShield.txt" , ItemLevel , CurrValue , "" , CurrTier )

}

AppendAffixInfo ( MakeAffixDetailLine ( A_LoopField , "Prefix" ,