Page 1 of 3 1 2 3 LastLast
Results 1 to 10 of 21

Thread: Example script: acquire_familiars

  1. #1

    Default Example script: acquire_familiars

    I was writing a script to obtain any mall-purchasable familiars I was missing, and realized that while it was fairly short and simple it was also using a fairly wide array of functions and would make a great example script. I noticed a couple people here lately who seem new to coding/scripting in general, so I added some comments.

    Below, and attached, is acquire_familiars.ash. It will purchase and grow any familiars you don't have that cost no more than your autoBuyPriceLimit, and will display any other familiars that are under 10M for you to consider manually purchasing. It is heavily commented with the intent of being a learning tool for those new to ASH.

    Code:
    // acquire_familiars.ash
    // Notepad++ with the 'ash' user defined language HIGHLY recommended!
    // Get Notepad++ from https://notepad-plus-plus.org/.
    // Get the ASH user-defined language mod from Bale's post at: http://kolmafia.us/showthread.php?3164-How-do-you-write-an-ASH-file
    
    // Automaticaly obtain any purchasable familiars that are less expensive than your autoBuyPriceLimit setting.
    void acquire_familiars() {
    	// can_interact() returns TRUE if we can interact with other players and the mall.
    	if( !can_interact() ) {
    		abort("This script is useless in-run!");
    	}
    	// Note this script does not actually check for mall access - it will fail for new accounts under a certain level.
    	// We don't worry about it because some edge cases really just aren't worth accounting for.
    	
    	boolean[familiar] expensives;
    	boolean[familiar] moderates;
    	// get_property(prop) always returns a string. It needs to be converted to the appropriate data type.
    	// Note that to_int( get_property("autoBuyPriceLimit") ) would also work. I generally prefer dot notation for type casting.
    	int buy_limit = get_property("autoBuyPriceLimit").to_int();
    	
    	// Loop through all existing familiars. 'fam' will reference each one in turn.
    	foreach fam in $familiars[] {
    	
    		// have_familiar( familiar ) is a method that returns TRUE if you already own the referenced familiar.
    		// So, "!have_familiar(fam)" does the opposite; TRUE only if you DON'T own the familiar.
    		// It could also be written as "!fam.have_familiar()".
    		// fam.hatchling is a property that returns the item the familiar is obtained from.
    		// item.tradeable returns TRUE if it can be purchased from the mall.
    		if( !have_familiar(fam) && fam.hatchling != $item[none] && fam.hatchling.tradeable ) {
    			// historical_price( item ) returns Mafia's stored price of an item, and will never check the mall.
    			// mall_price( item ) checks the mall for the price, but no more than once per day.
    			// Here we use the historical price if it's available, and check the mall only if it's not (returns 0).
    			int p = fam.hatchling.historical_price();
    			if( p <= 0 ) p = fam.hatchling.mall_price();
    			if( p <= 0 ) continue; // If the price is still 0, it isn't available from the mall for some reason.
    			
    			if( p <= buy_limit ) {
    				// We use retrieve_item first in case we have one somewhere grabbable, i.e. hagnk's or a clan stash.
    				// retrieve_item will also purchase the item from the mall IF autoSatisfyWithMall is on.
    				// It will abort the script if it fails UNLESS we grab its return value, so we store it
    				// in 'nothing' because we don't actually need it.
    				boolean nothing = retrieve_item(1, fam.hatchling);
    				// If we didn't get the item and autoSatisfyWithMall is off, it's because we didn't even try to mallbuy.
    				if( item_amount(fam.hatchling) < 1 && ! get_property("autoSatisfyWithMall").to_boolean() ) {
    					buy(1, fam.hatchling);
    				}
    				// If we still didn't get the item, we probably don't have any meat left. Abort.
    				// Note that our 'if' doesn't need curly braces {} if we're only executing one statement.
    				if( item_amount(fam.hatchling) < 1 )
    					abort("Failed to acquire " + fam.hatchling.to_string() + " - do you have enough meat?");
    				
    				// When I first wrote this, I used 'use', but it turns out there are hatchlings that have a different
    				// function when you use them. So instead I have to visit the 'grow' URL directly.
    				// I had to find this URL by looking at the [grow] link next to the item on the inventory page.
    				// Turned out it calls inv_familiar.php with the property "which" set to "3", and "whichitem" to the
    				// ID of the item in question. You can obtain an item's id by running it through to_int().
    				// Note that it returns the entire page, but we do NOT catch it this time; this is because we don't
    				// need it and would rather the script go ahead and abort if it fails for any reason.
    				visit_url("inv_familiar.php?pwd="+my_hash()+"&which=3&whichitem="+fam.hatchling.to_int().to_string());
    				//use( 1, fam.hatchling ); 
    			
    			// Below we deal with any familiars we aren't purchasing.
    			
    			// Prices above over 10M indicate Mr. Store or other expensive fams. We don't track those.
    			} else if( p > 10000000 ) {
    				continue;
    			
    			// If the price is between 2M and 10M, we'll display it as 'Expensive' for possible manual purchase.
    			} else if( p > 2000000 ) {
    				expensives[fam] = true;
    			
    			// If the price is below 2M but above autoBuyPriceLimit, we'll display it as 'Moderate' for possible manual purch.
    			} else {
    				moderates[fam] = true;
    			}
    		}
    	}
    	
    	// Because we're visiting the URLs directly Mafia may not always know what all we've grown, so let's 
    	// refresh the terrarium, just in case.
    	cli_execute("refresh familiars");
    	
    	// If we tracked any expensive familiars, display them.
    	if( expensives.count() >= 1 ) {
    		print("");
    		print("--- Expensive Familiars ----");
    		foreach fam in expensives {
    			// Print the familiar, its hatchling, and the hatchling's price.
    			// The to_string function can accept a Java formatting argument. The example below adds separating commas to an integer.
    			// More examples of Java string formatting can be found at: https://dzone.com/articles/java-string-format-examples
    			print( fam.to_string() + ", " + fam.hatchling.to_string() + ": ~" + fam.hatchling.historical_price().to_string("%,d") + " meat" );
    		}
    	}
    	
    	// If we tracked any moderate familiars, display those too.
    	if( moderates.count() >= 1 ) {
    		print("");
    		print("--- Moderate Familiars ----");
    		foreach fam in moderates {
    			print( fam.to_string() + ", " + fam.hatchling.to_string() + ": ~" + fam.hatchling.historical_price().to_string("%,d") + " meat" );
    		}
    	}
    }
    
    // When this script is called from the CLI, Mafia always looks for a main() function and runs that.
    // If we import this into another script, however, we'll want to be able to call it easily.
    // Therefore the primary use of this script is written as a separate function, which is called from main().
    void main() {
    	acquire_familiars();
    }
    Attached Files
    Last edited by Smelltastic; 04-24-2017 at 11:34 AM.

  2. #2

    Default

    Code:
    				// When I first wrote this, I used 'use', but it turns out there are hatchlings that have a different
    				// function when you use them. So instead I have to visit the 'grow' URL directly.
    				// I had to find this URL by looking at the [grow] link next to the item on the inventory page.
    				// Turned out it calls inv_familiar.php with the property "which" set to "3", and "whichitem" to the
    				// ID of the item in question. You can obtain an item's id by running it through to_int().
    				// Note that it returns the entire page, but we do NOT catch it this time; this is because we don't
    				// need it and would rather the script go ahead and abort if it fails for any reason.
    				visit_url("inv_familiar.php?pwd="+my_hash()+"&which=3&whichitem="+fam.hatchling.to_int().to_string());
    				//use( 1, fam.hatchling );
    While the game will always include which=X, that part of the URL is not actually required. You can probably include ajax=1 in the URL to not return the page (which should be slightly more server friendly, though you shouldn't be loading the URL enough to really matter), though that would require testing to make sure it works.

    For extra safety,
    Code:
    buy(1, fam.hatchling);
    could be
    Code:
    buy(1, fam.hatchling, buy_limit);
    This would prevent purchasing the hatchling if its price has suddenly gone up a lot since historical_price() was last updated. I suppose that would also mean updating other parts of the script in case this purchase actually fails.

    That's just some minor nitpicking of a script that looks pretty good.

  3. #3

    Default

    While the game will always include which=X, that part of the URL is not actually required. You can probably include ajax=1 in the URL to not return the page (which should be slightly more server friendly, though you shouldn't be loading the URL enough to really matter), though that would require testing to make sure it works.
    Originally Posted by lostcalpolydude View Post
    Thanks for this info; I'm not going to bother to update this - it's a teaching script, and I just wanted to let new people know how I got there and why - this is good to understand. I might do that in some other scripts of my own.

    Code:
    buy(1, fam.hatchling, buy_limit);
    This would prevent purchasing the hatchling if its price has suddenly gone up a lot since historical_price() was last updated.
    Originally Posted by lostcalpolydude View Post
    But since buy_limit is literally just the autoBuyPriceLimit setting, doesn't "buy" already default to this? Or is it only retrieve_item() that fails if it can't purchase below the limit?

  4. #4
    Developer
    Join Date
    Apr 2010
    Posts
    4,051

    Default

    But since buy_limit is literally just the autoBuyPriceLimit setting, doesn't "buy" already default to this? Or is it only retrieve_item() that fails if it can't purchase below the limit?
    Originally Posted by Smelltastic View Post
    Buy and autoBuy are different things. autoBuyPriceLimit comes in when using Acquire to get things, but Buy is explicit, and buys a thing regardless of the limit for acquire.

  5. #5
    Senior Member Theraze's Avatar
    Join Date
    Mar 2010
    Posts
    8,713

    Default

    Two aliases to do similar with less lines.
    buyfamiliars => ashq foreach i in $familiars[] {if (!have_familiar(i) && mall_price(i.hatchling) > 0 && mall_price(i.hatchling) <= %%) { buy(1, i.hatchling, mall_price(i.hatchling)); use(1, i.hatchling); } }
    listfamiliars => ashq foreach i in $familiars[] {if (!have_familiar(i) && mall_price(i.hatchling) > 0 && mall_price(i.hatchling) <= %%) print("You don't have "+i+" and it would cost about "+mall_price(i.hatchling)+" to fix that");}
    Run listfamiliars 100000 to see all unowned familiars under 100k. Run buyfamiliars 100000 to purchase and grow them. There's also a get version I can post if anyone actually cares.

  6. #6
    Senior Member VladYvhuce's Avatar
    Join Date
    Feb 2016
    Location
    Kansas
    Posts
    215

    Default

    Hmm... So... What would I need to trim it down to if I just wanted to check familiar prices, without the buying function?

  7. #7
    Developer
    Join Date
    Aug 2009
    Posts
    2,675

    Default

    Just call listfamiliars? There are two aliases provided.

  8. #8
    Senior Member Theraze's Avatar
    Join Date
    Mar 2010
    Posts
    8,713

    Default

    Assuming that Vlad wanted to run the acquire_familiars script to categorize things without actually buying rather than ignoring my post, but...

  9. #9
    Senior Member VladYvhuce's Avatar
    Join Date
    Feb 2016
    Location
    Kansas
    Posts
    215

    Default

    I'm not sure how to implement those. Everything I've tried results in "Unable to invoke listfamiliars". Whatever I'm doing, Mafia doesn't like it.

  10. #10
    Developer
    Join Date
    Apr 2010
    Posts
    4,051

    Default

    Add alias to the front of the lines given to add them as aliases

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •