Register
View RSS Feed

//no comment

Custom Price Magic

Rate this Entry
For those who aren't aware, TSM2.0 introduces the ability to use custom prices. In 1.x, you were limited to simple percentages of price sources (ie auctiondb, crafting cost, wowuction, TUJ, etc) or fixed gold amounts. In 2.0, this has been expanded greatly. You can now enter complex expressions involving multiple price sources, arithmetic operations, min/max functionality, and much more. Parsing these custom price strings is a relatively complex process

For the purposes of explaining how the magic behind the scenes works, I'll use this color to denote things we are storing in memory, and this color to denote what the custom price string looks like after the step. Here is the custom price we will use for this example:
50*min(DBMarket(item:72120), 110% crafting(item:72120), 5g)

This is a relatively arbitrary example, but contains a good variety of the custom price features. Let's first look at what it's doing. We first note that "item:72120" is the itemstring for . So, the parameters of the min(...) function are: auctiondb market value of , 110% crafting cost of , and a fixed 5g. Whatever the minimum of those three things is will be multiplied by 50 to get the answer. Note that normally you wouldn't specify the item for each price source (ie dbmarket and crafting), which would cause it to use whatever item the price source is being applied to (ie the item you're posting / crafting / etc), but I digress. Let's look at what TSM does with this in order to get it in a form that lua can understand and can be used by TSM. Note that there are many verification and error checking steps that I'll be skipping for the purposes of this explanation.


1) The first thing we do is it make everything lower case. This way everything becomes case insensitive.
50*min(dbmarket(item:72120), 110% crafting(item:72120), 5g)

2) We replace a single fixed gold value (a maximum of 1 may be present in any custom price string) with some placeholder text and store what that gold value was (in copper).
50*min(dbmarket(item:72120), 110% crafting(item:72120), ~gold~)
goldAmount = 50000

3) We will now look for convert(...) calls. There are none in this example so nothing changes.
50*min(dbmarket(item:72120), 110% crafting(item:72120), ~gold~)
goldAmount = 50000

4) All specific items (either itemstrings in the form "item:<itemID>" or full item links) are replaced with some placeholder text and stored in a list. In this example, the same item is used twice, but we still store it twice.
50*min(dbmarket(~item~), 110% crafting(~item~), ~gold~)
items = {"item:72120", "item:72120"}
goldAmount = 50000


5) To make the job of parsing things a little easier, we make sure all spacing is consistent. This involves a couple steps at once, so bear with me. It will put a space on either side of mathematical operators, either side of commas, and between price sources and "~item~" placeholders. We also remove parenthesis around "~item~" placeholders.
items = {"item:72120", "item:72120"}
50 * min ( dbmarket ~item~ , 110% crafting ~item~ , ~gold~ )

goldAmount = 50000

6) "110%" is not a mathematical operation which lua can understand, so we convert this to a multiplication.
50 * min ( dbmarket ~item~ , 1.1 * crafting ~item~ , ~gold~ )
items = {"item:72120", "item:72120"}
goldAmount = 50000


7) I've left out most of the verification / error checking steps, but this is one of the primary ones. The string is now in a form where it can easily be split into parts with a single space separating each part. There's only a finite number of things which each part can be, so we check each one and yell (return an error message) if there's something we didn't expect (perhaps a misspelled price source). Nothing changes with this step assuming there are no errors.
50 * min ( dbmarket ~item~ , 1.1 * crafting ~item~ , ~gold~ )
items = {"item:72120", "item:72120"}
goldAmount = 50000


8) Now that we're sure this custom price is valid and certain (fairly certain...) there won't be any nasty lua errors later on, we can work towards putting this in a form which lua can understand. The first step now is to put any price source components into the order and form expected by the TSM function which will evaluate them.
50 * min ( ("~item~","DBMarket") , 1.1 * ("~item~","Crafting") , ~gold~ )
items = {"item:72120", "item:72120"}
goldAmount = 50000


9) Next, we put the specific items back in for the placeholders. We're now done with items.
50 * min ( ("item:72120","DBMarket") , 1.1 * ("item:72120","Crafting") , ~gold~ )
goldAmount = 50000

10) Now, for various reasons including being able to detect price sources which don't return a value, we will be calculating each price source before evaluating this expression. Each calculated price will be put into a "values" array, so we now insert the valid index into that array into the string and store what we're replacing into a new list (with some slight formatting changes to make it look like a valid lua list).
50 * min ( values[1] , 1.1 * values[2] , ~gold~ )
itemValues = { {"item:72120","DBMarket"}, {"item:72120", "Crafting"} }
goldAmount = 50000


11) We would now deal with convert(...) placeholders, but there are none here so nothing changes.
50 * min ( values[1] , 1.1 * values[2] , ~gold~ )
itemValues = { {"item:72120","DBMarket"}, {"item:72120", "Crafting"} }
goldAmount = 50000


12) We now deal with the ~gold~ placeholder by placing the copper value from goldAmount back in.
50 * min ( values[1] , 1.1 * values[2] , 50000 )
itemValues = { {"item:72120","DBMarket"}, {"item:72120", "Crafting"} }

13) For reasons I won't get into, we are going to use a custom version of lua's min(...) function, which is named _min(...). So, we swap it out.
50 * _min ( values[1] , 1.1 * values[2] , 50000 )
itemValues = { {"item:72120","DBMarket"}, {"item:72120", "Crafting"} }

14) We're done! This is now in a form which lua can understand. The last step is to insert it into a template and then create a function which can be called by any code which needs to evaluate this custom price. There are many things besides this expression which are included in that template (such as computing all the item values from the itemValues list, implementing our special _min function, etc), but I won't get into those here. If you're interested in what the template looks like, I've posted it here (the first %s gets replaced by the itemValues array and the second by the custom price string).

Hope you followed all of that. Feel free to ask any questions you may have and I'll try my best to answer them.

Comments

  1. Kathroman's Avatar
    Interesting. I have a feeling these "function" values for pricing are really going to come to the forefront once we start to get the hang of things. I know that I've personally got that custom pricing page from the Wiki bookmarked