ZLib -- Zarqon's useful function library


Active member

What is it?
ZLib is capacitors, pistons, and servos. ZLib is a box of assorted LEGOs. ZLib is a stocked pantry. ZLib is evolution towards order but without form.

Or, less magniloquently, Zlib is a single function library containing my most useful functions. I grew tired of tweaking a function that was in multiple scripts and making sure I had changed them all. It would be nice, thought I in my customary italics, to have it in just one place. Thus, this library, which can be easily imported into any script. In choosing which functions to include, I have tried to select only those functions which are a) super useful, or 2) needed by another of the included functions.

Basically, ZLib is inventing the wheel. It makes it much easier for scripters to do things like make bicycles, cars, and monster trucks.

For Users

First, an important message:

Are you getting this error?

Expression syntax errors for 'modifier_eval()':
Expected <something>, found <something else> (zlib.ash, line 188)

If so, it's not ZLib's fault. Repeat, ZLib is not responsible, so do not report that error in this thread. It means that a script which includes ZLib sent a bad formula to ZLib to be evaluated. In the vast majority of the cases I've seen so far, the responsible script is BatBrain, so please go report the error there rather than here. Thank you.

Now to get down to it. Quite a few scripts on this forum use functions which are in ZLib, meaning you should have zlib.ash somewhere in your scripts directory. Some scripts which update via SVN will automatically install ZLib for you (in case you're wondering where it came from). A lot of scripts are also using ZLib's "script settings", which is a way to keep script configuration variables stored locally, outside the script. This allows you to configure scripts without needing to actually edit them -- and even when you update the script your settings for that script will remain unchanged. You can type "zlib vars" into the CLI to see your current ZLib settings and their values. You can also edit these settings by calling ZLib directly, as described here. Other than editing settings, you won't actually "run" this script, but a lot of other scripts will.

For Script Authors

I feel pretty confident that nearly all of these functions are useful and have broad applications. With ZLib you can iterate through your kmail easily, make sure your users keep your script current, or allow your script to always use a current version of a publicly-editable data file. You can work more easily with strings, specify verbosity levels for individual print commands, and display numbers in a human-readable way. ZLib even gives you some functions you might miss from other programming languages like abs(), minmax(), and join(). It's very easy to avail yourself of these functions, and add commonly useful functionality to your script without having to do all the legwork from scratch. StDoodle has been so good as to add ZLib documentation to the ASH Wiki -- so I will be maintaining the function list there rather than in this thread.

You can type "ashwiki zlib" in the CLI to visit that page easily from within mafia.

How to use it:
To include this library in your script, simply add the following at the top of your script:
import <zlib.ash>
Then all these functions will be available in your script. Have fun!



To download this script, simply type
svn checkout https://svn.code.sf.net/p/zlib/code
in the CLI. Mafia will automatically install it in your scripts directory.

6.14.09: r1 posted.
7.11.09: r2: added my_defstat().
7.25.09: r3: Introduced setvar() as a solution to per-character script settings. Also added spaces_to_underscores() and minmax(), and versions of send_gift() and kmail() that allow you to specify the message inside gifts, for users that need that degree of control. Some coding tweaks to get_safemox() and my_defstat(). Call ASH's recent set_location() in obtain().
7.28.09: r4: Include some more settings: automcd, is_100_run, and defaultoutfit. Include new old function auto_mcd().
9.01.09: r5: New int setting unknown_ml (default is 150) -- auto_mcd() uses this as the assumed strength for unknown or combatless zones. FTF will also use this as the ML of unknown monsters. Added error(). kmail() now saves messages in your outbox.
9.26.09: r6: added script settings editing functionality when called directly. Removed spaces_to_underscores(), since ASH's replace_string() is better.
10.10.09: r7: resist() now takes advantage of Exotic Parrot. auto_mcd() skips MCD-sensitive boss zones. Mostly fixed a bug with the return value of obtain() for choiceadvs. Also, some changes for familiars: first, the is_100_run setting is now a familiar, rather than a boolean (default is none). Second, best_fam() now can prioritize familiars.
10.19.09: r8: removed min() and max() because they were added to ASH! w00t! Also changed minmax() to use floats (will not require script editing and allows for greater precision when needed). Fixed error calculating possible parrot weight (had accidentally swapped the parameters for numeric_modifier()).
11.5.09: r9: added eval(). Converted check_version() to merely wait an annoying interval rather than popping up an annoying dialog. Here's hoping it doesn't facilitate lots of bogus bug reports. Also, it now uses a regex provided by Bale, which I inserted using tweezers and rubber gloves. Also you can now type "zlib vars" to see your current settings.
11.15.09: r10: added auto_mcd(int safemox) for cases where you want to fight a single monster (i.e. item-use-invoked combats).
12.03.09: r11: also added auto_mcd(monster mob) as a shortcut for the above. You now have a full range of auto-MCD options. Kmails (and gifts) no longer change the message when splitting kmails with more than 11 types of item.
12.30.09: r12: added abs() for both ints and floats. Added vprint() as a verbosity-sensitive wrapper for print() and abort(). Deprecates error()
1.23.10: r14: added vprint_html() to complement vprint(). Added load_current_map() for use by scripts to auto-update data files using the Map Manager.
2.07.10: r15: revert vprint(msg,0) to abort() rather than exit due to unwanted results when vprint's return value was captured. Add has_goal() for both items and monsters. Auto-MCD-ing skips Slime Tube. As forewarned, removed error().
2.27.10: r16: don't url_encode() kmail messages, now that mafia appears to handle that automatically. Smarter MCD-ing (now also skips for monsterless zones, a few other tweaks).
3.21.10: r17: excise() now accepts empty strings for both start and end to specify the ends of the string. MCD-ing now uses unknown_ml correctly (stupid parenthese). minmax() actually uses min() and max(). Sending only meat in a gift now works correctly. Stop calling ZLib releases "builds" in the changelog since they are not compiled -- instead use "r" for "release". Seriously, I was annoying myself with that.
4.19.10: r18: added rnum() for human-readable numbers. Removed all the commented WOSSMAN stuff since relay is the future of ZLib settings editing.
4.25.10 r19: fixed recursion bug in has_goal(), and added versions for monsters and locations. All three versions now return floats.
5.01.10 r20: Happy May Day! Fixed has_goal() for bounty items. Removed to_element() since it is now in ASH. Auto-MCD now uses appearance_rates() to determine which monsters to skip, with a few choiceadv-sensitive exceptions. Now locations like Haunted Bathroom (GMoB) and Primordial Ooze (Cyrus) will MCD correctly.
11.10.10 r21: Happy payday! (Well, for me at least.) Added normalized(). Normalize ZLib settings in setvar(). Recent KoL changes allowed for solving the moxie sign MCD detection issue. Account for combat frequency modifier in has_goal(location). Changes to check_version(): it now returns a string, usable by relay scripts. Allow for specifying a required mafia revision number in the checked thread.
1.26.11 r22: ASH's exponentiation operator will shortly be **, not ^. Consider unspaded drops as having a barely nonzero drop rate, rather than 0. Fix regex in check_version(). And: normalized() can now normalize comma-delimited lists of mafia types.
2.16.11 r23: emergency fix for new Knob locations.
5.01.11 r24: Added load_kmail(), isxpartof(), and join(). Given the new kmail record type, added a kmail(kmessage). The new function also allows has_goal() to return percentages for ingredients of goals.
5.12.11 r25: Allow for negative fromid's in load_kmail(). Normalization of script settings is case-sensitive. Print clicky link in check_version(), and try to be a little bit smarter about versions formatted n.n.n and suchlike.
5.17.11 r26: Added equals(string, string) as a case-sensitive == operator for strings. Further tweaks to check_version(): 1) smartly handle post-version-check updates, and b) only run the comparison logic if the strings are actually different.
5.18.11 r27: Added be_good(item) for checking whether an item will anger bees. Incorporate that into use_upto(). ASH's in_mysticalty_sign() is now canadia_available().
6.06.11 r28: More sign changes for auto_mcd(). Added really cool function process_kmail() for really simple kmail parsing.
8.02.11 r29: Added coinmaster type to setvar() and normalized(). Only remove HTML bits from kmails that include items/meat. When called multiple times in a row, don't parse already deleted kmails in process_kmail().
9.16.11 r30: Never purchase items in use_upto() in Fistcore, regardless of the purchase parameter. Change be_good() to accept a string and filter 'b' familiars in best_fam(). Make has_goal() speculative. Rather than just show an error message, print a substring-matched list of settings when called from the CLI with text that doesn't match a setting name.
10.15.11 r31: Added phylum type to setvar() and normalized(). Previous update's matching should have been case-insensitive. Eliminate infinite recursion in isxpartof(). Move all version checking into a single map, for true once-daily checking.
11.28.11 r32: Significant repurposing of be_good(): it now checks that the supplied string is safe for whatever path you're on, not only Beecore. Add commas in rnum() starting at 1000, not 10,000. Allow obtain() to get flyer ML and insults now that those are valid conditions. Make my_defstat() speculative.
12.02.11 r33: Add optional usespec parameter to my_defstat(). If true, the return value will be speculative. If false or no value is supplied, the value will be non-speculative, as before.
2.16.12 r34: Avatar of Boris support for be_good(). Allow "autostop" as a condition for obtain().
3.17.12 r35: Huge speed improvement to all forms of has_goal() due to ASH's new goal functions. Additionally, all functions that previously used speculation now include optional usespec parameter, a la my_defstat(). Only perform "whatif quiet" to initialize values if spec is empty.
5.09.12 r36: New function tower_items(). Consider empty maps equivalent to out-of-date maps in load_current_map(). Fix to has_goal(location) for areas with unknown combat rates.
7.12.12 r37: Fix rnum() after mafia's change to floats. Introduce two new functions for finding the worth of items: mall_val() and sell_val().
6.17.13 r38: ZLib is now hosted on SourceForge, and accessible with mafia's new SVN capability.

Since migrating to SVN, the changelog for updates made after 2012 can be found on SourceForge. However, I will continue to post more in-depth release notes in this thread.
Last edited:


New member
Excellent! On the off chance that you'd like to include any of the functions from one of my scripts (including ascend.ash), please feel free to do so. One suggestion: Alhifar's excellent run_adv(). I have a slightly modified version of it in ascend.ash, and I find it to be very handy.


If you would like to include run_adv(), let me find where I put the updated version, the version posted earlier on here had a bug or two that I have corrected.


New member
Important question: How do we handle the issue of script A including script B, when both A&B include zlib? You can see how this is a challenge for something like ascend.ash, which includes many subscripts, many of which are also used independently.

Another suggestion: tools related to safemox. Determine safemox for an area, find the difference between safemox and what you've deemed acceptable via threshold, etc.

Another another suggestion:

int VERBOSE = 2;
boolean verbose(int lvl,string msg)
if (lvl <= VERBOSE)
print("Level "+lvl+": "+msg);
return true;
return false;
I've often meant to put this in a common include, so I can include copious debug statements that can be easily eliminated from actual operation.


The following bit of code should handle any choice adventure supported by mafia, as well as combats, and regular non-combats. It will return the full text of the LAST page visited by the function, just as run_combat() does.
string run_adv( string page_text )
	while( contains_text( page_text , "choice.php" ) )
		matcher m_choice_adv_num = create_matcher( "whichchoice value=(\\d+)" , page_text );
		string choice_adv_num = m_choice_adv_num.group( 1 );
		string choice_adv_prop = "choiceAdventure" + choice_adv_num;
		string choice_num = get_property( choice_adv_prop );

		if( choice_num == "" ) abort( "Unsupported Choice Adventure!" );

		string url = "choice.php?pwd&whichchoice=" + choice_adv_num + "&option=" + choice_num;
		page_text = visit_url( url );
	if( contains_text( page_text , "Combat" ) ) page_text = run_combat();
	return page_text;


New member
Excellent work Alhifar. For unsupported choiceadvs, can we do something more robust than abort()? It'd be nice to print an error and continue operation.


What is there to continue? You would be stuck in an infinite loop in the choice adventure, because it doesn't know what to choose. I personally wouldn't be comfortable having it make a default choice.

In an ideal world, I would be able to open up the mini-browser to the page in addition to aborting, but that seems like it would be way more work than it is worth for Veracity/Jason.


Active member
Basically, every script and their mom can include ZLib without causing trouble. (Yes, the scripts' moms.) Mafia seems to intelligently recognize whether or not a file has already been included, and will not include it twice. So, no worries -- feel free to go include-happy!

Alhifar -- nice! I like how it also appears to handle choiceadvs that lead to combats.


It does. I added that in as a result of the bug that had occurred when it was run on a choice adventure leading to a combat; it would cause mafia to be extremely confused.


New member
(Crossposting from the wossname script since it belongs here)

It would be nice to have a function that encapsulates the behavior you use with threshold (if it's not set as a property, prompt the user and assume a default). I would use this in my scripts - for example, eatdrink.ash. Every time someone gets a new version, they blow away the script settings they've changed, which I'm sure gets old. I imagine it would work like this:
default_prop(foo,bar); //the default property for "foo" is "bar"
get_prop(foo); //if foo is set manually in the user's preference file, return the setting; otherwise, return the default set via default_prop


New member
Another request: A global setting to disable version prompting. If I'm running unattended for a while, I don't want version check popups. :)


Ditto that! I sometimes find the pop ups annoying. Would like to stop it. How about just printing out a red message about the version being out of date, then doing it anyway without prompting.


New member
I propose a new function:

// Returns the property if set, or a default if not
string get_preference(string pref, string pref_default) {
  string result = get_property(pref);
  if (result=="")
    print("You have not set property '"+pref+"', so the default value of '"+pref_default+"' will be used.");
    return pref_default;
  return result;

If scripts use this (e.g. for things like warplan and other preferences) then your preference customizations won't be wiped out when you upgrade, but there wills till be sensible defaults for a first time script user. So your threshold initialization, for example, becomes:
// set safetyThreshold, which is used by many of my scripts
int threshold = to_int(get_preference("safetyThreshold", "4"));

I would LOVE it if you did this, since I always have to recheck my settings every time I upload a script to make sure my defaults won't screw up anyone else...!


Active member
@dj_d: Hmmmm.... you've got me thinking. I'll have to think about it more before implementing anything though. I think there must be a super awesome way to do this. Possibly something involving a global map of character preferences, rather than using mafia settings... or perhaps storing preferences on a remote server! :)

Ditto that! I sometimes find the pop ups annoying. Would like to stop it.

They're meant to be annoying. How to stop it: download a current version. I had earlier complaints from users not knowing whether or not they had the latest and greatest. And I've had to deal with quite a few users reporting problems they encounter using outdated versions. This particular user annoyance exists for my benefit. On the plus side: at least now it's only once daily!

If you, being an advanced user, would like to bypass the alerts, change the user_feedback() command in the version_check() function to a print statement yourself.


not as exciting as everyone elses, but heres 2 functions I use in more than one script, not sure quite how useful they are to people.

class parseclass(string c) {
	class cls=to_class(c);

	if(cls==$class[none]) {
		switch(c) {
			case "tt":
			case "turtle":return $class[Turtle Tamer];
			case "sc":
			case "seal":return $class[Seal Clubber];
			case "pm":
			case "pasta":return $class[Pastamancer];
			case "sauce":return $class[Sauceror];
			case "db":
			case "disco":return $class[Disco Bandit];
			case "at":
			case "accordion":
			case "accord":return $class[Accordion Thief];
			default:return $class[none];
	return cls;

and probably somewhat pointlessly:

string pluralise(item i, int amount) {
	if(amount==1) {
		return to_string(i);
	return to_plural(i);

I demand inclusion! or bat part presents will stop! mwuahahahah!

actually not really, just my penny worth.


Active member
About ZLib "Script Settings"

(edited 5/14/2017 to account for new handling of defaults)

There are now two functions which allow for per-character script settings:

void setvar(string varname, mixed dfault)
string getvar(string varname)

setvar() initializes a script variable by setting its default value and type in vars_defaults.txt. This only happens once, or again if your default gets deleted somehow. And once it's initialized, users can change its value by running ZLib in the CLI. Any values they change are saved per-character in vars_<myname>.txt. Your script can access the value of this setting (the user's changed value or otherwise the default) by calling getvar(). Several interesting possibilities now present themselves to users and script authors.

For Users
  • Script settings are now all saved in one place, separate from mafia settings. I've read more than one post wishing that script-defined settings and mafia settings would be separate. This provides a solution.
  • Script settings are independent from scripts. This means that you will no longer need to edit scripts to adjust your setttings. Further, when you download a script update, the script will use your saved settings and you won't need to reset them!
  • To see all of your current settings, type "zlib vars" in the CLI. To change a setting, type "zlib settingname = value". If you're adjusting threshold, you can use "up" or "down" as the value to adjust your threshold relatively. This is almost exactly as convenient as mafia settings (possibly more so since you don't need to open a text file to find setting names!).
  • If for some reason you prefer to open a text file, ZLib settings are stored in a file called vars_defaults.txt in your data directory. Those which you have changed from the default value are saved in vars_<myname>.txt.
  • Scripts that use Zlib script settings will initialize the settings -- saving their default value and type in vars_defaults.txt -- when you run them for the first time. Attempting to edit a nonexisting setting won't work, so you'll need to run a script once (then, usually, mash the ESC key before it actually does anything) before you can configure it. Script documentation should tell you which settings to change to get your desired behavior.

For Script Authors
  • Use setvar() to initialize a setting that your script will reference. To reference the setting, use getvar() and convert from string to whichever type the setting is supposed to be. Accessing vars[] or vardefaults[] directly (the previous method) is not recommended, as a value may exist in one but not the other, or both.
  • Script settings may now be used across scripts, in exactly the same way that mafia settings are. Basically, this works almost exactly like mafia settings, except that new settings can only be created by setvar() or manually editing the file ("zlib nonexistentsetting = value" will fail).
  • Settings are only stored if you run a script that defines/uses them. So your settings file will not contain any extraneous unused settings.
  • Script authors can now test for a setting's existence in vardefaults[], which means you can check to see if a user has used a given script. It's almost as good as a script_exists() function. This can allow scripts to work together with other scripts, if they exist!
  • Scripts with overlapping or related functionality can be designed to access a single shared setting, in much the same way that my scripts have until now all shared a "threshold" mafia setting. Changing a single setting can now change the behavior of every script that accesses that setting.

The Gory Details

When importing ZLib, it loads the setting defaults from vars_defaults.txt and a map of your script settings that have been changed from vars_<myname>.txt. To access a script setting within an ASH script, use getvar(varname).

When a script calls setvar("threshold",4), ZLib checks to see if a setting called "threshold" already exists. If so, since dfault is an integer, it ensures that the value is an integer using normalize() (saving changes if necessary), but unless normalization changed the value, nothing else happens. If "threshold" does not exist, it initializes its default value to 4, its type to int, and saves those defaults back to vars_defaults.txt. The setting may now be accessed by getvar() or edited using ZLib in the CLI.

The dfault parameter can be any primitive or ASH type ($item, $effect, etc), but not a map, array, or record.

Choosing Setting Names

The file of script settings will contain all script settings, sorted alphabetically. Also, there is no way to detect if a setting is unused, so if you decide to change the name, the old setting will never be deleted. Please think carefully about your setting names. If you have a setting named "setting1", a user will probably not have a clue which script that is for or what it does. True, this can be overcome with documentation, but it is far better to have settings that make sense just by looking at them.

My recommendations:

1. Use a name that clearly identifies what the setting is/does.

2. Prefix your setting names with a script identifier. For example, here are some of my One-Click Wossname script settings:


Those settings which are specific to OCW are prefixed with "ocw_" so as to be found together in the settings file. However, some of the settings are usable across scripts, and are not so prefixed. For example, I intend to use the "is_100_run" variable in every script that swaps familiars, so as to avoid tainting a 100% run. Likewise, the "defaultoutfit" will be used by nearly all of my adventuring scripts that swap outfits (MacGuffin, OCW).

That's it. Enjoy this new functionality!
Last edited: