Relay Override Scripting

From KoLMafia
Jump to: navigation, search

A relay script is a script that creates or modifies a web page in the relay browser. It is only in the relay browser that the effects of these scripts can be seen. There are two different kinds of relay override scripts. The first is simply known as an Relay Script. The second is a User Interface script.

Relay Script

Basic relay scripts are used to change the appearance of a KoL page. You will find many examples of relay scripts that you may download from the KoLmafia Forums to your /relay directory.

Writing a Relay Script

For a simple Relay Script, there are several basic rules:

  1. The name of the script must be the same as the page it is overriding, except with an ash extension instead of a php extension. For users who have enabled relay override scripts, mafia will automatically call xyz.ash every time the relay browser attempts to visit xyz.php.
  2. Unless the script will display entirely new content, it should first load the unmodified page from KoL using the command visit_url() with no parameters.
  3. Then the contents of the page can be modified with replace_string() or other string manipulation functions.
  4. Finally, the modified page is written with the write() command.

Whichplace, Whichshop and Whichstore Overrides

For overrides to place.php, shop.php and store.php it is possible to override based on the whichplace, whichshop and whichstore parameters. This is quite helpful so that you can choose to override only specific places and shops without needing to make an override for all of them.

An example is that to override the plains, you'll note that the URL is place.php?whichplace=plains so you could override all place.php pages with a place.ash override, or you could choose to override only the plains with a script named place.plains.ash.


Example of Relay Override

Here is an example of a relay script. Assuming it is named shop.shore.ash, it will modify "The Shore, Inc." Shop at shop.php?whichshop=shore.php to reveal items that are needed for the level 6 tower monster.

// This script will only work if it is named shop.shore.ash
buffer telescope(buffer page) {
   
   void dynamite() {
      if(available_amount($item[stick of dynamite]) < 1) page.replace_string("<b>dude ranch souvenir crate</b>",
         "<font color=\"green\"><b>dude ranch souvenir crate<br />(stick of dynamite needed)</b></font>");
   }

   void orchid() {
      if(available_amount($item[tropical orchid]) < 1 && available_amount($item[packet of orchid seeds]) < 1) page.replace_string("<b>tropical island souvenir crate</b>",
         "<font color=\"green\"><b>tropical island souvenir crate<br />(tropical orchid needed)</b></font>");
   }

   void fence() {
      if(available_amount($item[barbed-wire fence]) < 1) page.replace_string("<b>ski resort souvenir crate</b>",
         "<font color=\"green\"><b>ski resort souvenir crate<br />(barbed-wire fence needed)</b></font>");
   }
   
   if(!can_interact() && my_path() != "Bugbear Invasion") {
      if(my_path() == "Bees Hate You") {
         orchid();
      } else if(get_property("telescopeUpgrades") == "7" && !in_bad_moon()) {
         switch(get_property("telescope7")) {
         case "see a wooden beam":
            dynamite(); break;
         case "see a formidable stinger":
            orchid(); break;
         case "see a pair of horns":
            fence(); break;
         }
      } else {
         dynamite();
         orchid();
         fence();
      }
   }
   
   // If dinghy plans are needed, you will always need to buy them! Otherwise it is time to look into that UV-resistant compass 
   if(get_property("lastIslandUnlock").to_int() < my_ascensions() && page.contains_text("dinghy plans"))
      page.replace_string("<b>dinghy plans</b>", "<font color=\"green\"><b>dinghy plans</b></font>");
   else if(available_amount($item[UV-resistant compass]) + available_amount($item[ornate dowsing rod]) == 0)
      page.replace_string("UV-resistant compass", "<font color=\"green\"><b>UV-resistant compass</b></font>");
   
   return page;
}

void main() {
   visit_url().telescope().ShopWTF().write();
}


User Interface Script

There are special kinds of relay scripts; scripts with user interfaces. As with a regular relay override script, they go in the "relay" directory. However, they don't override other pages; their entire content is created from scratch. These scripts can be accessed under the relay drop-down in the bottom-right of the top pane. Their names must start with "relay_" in order for them to show up there.

This can provide a pretty interface and automate tasks, but relay scripts aren't really suitable for long, ongoing processes such as autoadventuring, since nothing appears in the browser until the script exits. Also, writing them requires a rather different mindset than the sequential execution of a normal script: if the relay script presents options for the user, they are actually read by an entirely separate invocation of the script than the one that displayed the options. The areas in which relay scripts will be the most useful are user-friendly configuration of other scripts, and presentation of options for the user to carry out in the relay browser themselves since the script can simply link to any in-game activity.

Writing an Interface Script

For writing interface scripts, several features were added to KoLmafia.

  • ASH predefined string constant __FILE__, which is the filename of the current script. This allows relay scripts to link or submit a form to themselves, even if the user has renamed them.
  • ASH function form_fields(), which returns a string[string] map with all the name/value pairs from the relay request being handled.
  • ASH string functions entity_encode() and entity_decode(), for converting text that might contain special characters such as ampersands and angle brackets so that it can safely be displayed in HTML, and retrieving such text from form fields.

Additionally, there are some excellent tools for writing form elements in interface scripts on the KoLmafia Forum. That script is a toolbox of essential functions for any advanced interface script so make good use of it.

Example of User Interface

Here is an example of a User Interface script. This must be saved in /relay with a name starting with relay, such as relay_breakables.ash to appear in the relay drop-down in KoL's top menu. This was originally presented by Jason Harper here and makes an excellent example.

// The following constant can be edited to add more breakable items. 
// However, please note that adding items here does no good at all
// unless KoLmafia has been updated to recognize the specific breakage
// messages of those items.
boolean[item] breakable = $items[
   antique helmet, antique spear, antique shield, antique greaves,
   cyber-mattock, cheap studded belt,
   sugar chapeau, sugar shank, sugar shield, sugar shillelagh,
   sugar shirt, sugar shotgun, sugar shorts];

void write_option(string label, string value, string current) {
   write("<option value='");
   write(value);
   write("'");
   if (value == current)
      write(" selected");
   write(">");
   write(entity_encode(label));
   writeln("</option>");
}

void write_select(string label, string pref, boolean addDefault, string[string] fields) {
   if (fields contains pref)
      set_property(pref, fields[pref]);
   
   string current = get_property(pref);
   write("<tr><td align=right>");
   write(label);
   write("</td><td><select name='");
   write(pref);
   writeln("'>");
   
   if (addDefault)
      write_option("Use default", "", current);
   write_option("Abort on breakage", "1", current);
   write_option("Equip previous item", "2", current);
   write_option("Re-equip from inventory (if available), or abort", "3", current);
   write_option("Re-equip from inventory, or equip previous item", "4", current);
   write_option("Acquire new item & re-equip", "5", current);
   writeln("</select></td></tr>");
}
   
void main() {
   write("<html><body><form method=POST action='");
   write(__FILE__);
   writeln("'>");
   write("Breakable equipment fine tuning v1.0, by jasonharper@pobox.com");
   writeln(" (in-game: <a href='sendmessage.php?toid=363053'>Seventh</a>)");
   write("<p>");
   write("This script lets you override the default behavior when equipment breaks in combat,");
   write(" on an item-by-item basis.  The default setting (found in the Item section of Choice Advs)");
   writeln(" is duplicated here for your convenience.");
   writeln("<table border=0>");
   string[string] fields = form_fields();
   write_select("Default", "breakableHandling", false, fields);
   foreach itm in breakable
      write_select(to_string(itm), "breakableHandling" + to_int(itm), true, fields);
   writeln("</table>");
   writeln("<input type=submit value='Save changes'>");
   writeln("</form></body></html>");
}

Advance Techniques for Interface Scripting

Adding CSS Styles:

  • To have a fancier Relay Script, one can add CSS styles to certain elements. Example of this can be found Bale’s OCD Manager Relay script.

Tools:

  • Quackit has a lot of basic info on HTML/CSS/etc. It also has a decent WYSIWYG editor for basic styling and layouts.
  • JSFiddle allows you to enter HTML, CSS, and java script and run it all to see what it creates. It also has a handy “TidyUP” feature which will auto-indent HTML and will highlight HTML tags in red if they are improper which is great for debugging.
  • CSS Generator Offers quick CSS for buttons/paragraphs/tables/etc for more advance styling options.
  • CSS Table Generator is an excellent resource for CSS Tables designs.
  • RegexPlanet is a handy tool to convert CSS/HTML into a Java string. After writing/generating HTML/CSS for your relay script, it can be a pain to “convert” it all by hand into text that will work well within the .ash Relay Script. Instead, you can copy/paste it all into Regexplannet as a Regular Expression. After clicking Test, it will throw up some error (obviously), but will still convert it all into a Java string, which is what you want. Copy paste “as a Java string” into your relay script, after a page.append, and you should be good to go. You can then break it up for easier reading, but it is not necessary.

Example of Using CSS in a User Interface Script

Note: Instead of using the function write, the example will use a buffer called page which everything will be appended too.
Note 2: Example also shows why it is a good practice to use \" instead of single quotes when typing HTML. Using \” will prevent values containing a single quote from messing up.

////////// Beginning of form functions based strongly on jasonharper's htmlform.ash from http://kolmafia.us/showthread.php?3842
string[string] fields;	// shared result from form_fields()
boolean success;	// form successfully submitted
buffer page;

boolean test_button(string name) {
	if(name == "")	return false;
	return success && fields contains name;
}

//This Example shows what it is a bad idea to use single quotes instead of full quotes for values
boolean write_button_Example(string name, string label) {
	page.append("<input type=\"submit\" name=\""+name+"\" value='"+label+"'/>");
	return test_button(name);
}

//Jason's Button re-written to use page.append instead of write
boolean write_button(string name, string label) {
	page.append("<input type=\"submit\" name=\""+name+"\" value=\""+label+"\"/>");
	return test_button(name);
}

//Added a class to the button, designating a class will allow all buttons of that class to be changed by that class's CSS
boolean write_button2(string name, string label, string css_class) {
	page.append("<input type=\"submit\" name=\""+name+"\" class=\""+css_class+"\" value=\""+label+"\"/>");
	return test_button(name);
}
boolean write_button2(string name, string label) {return write_button2(name, label, "");}
////////// End of jasonharper's htmlform.ash


void styles() { //Function that contains all CSS
	page.append("<style type='text/css'>"+
	//Shiny button CSS, turned into a convenient Java String. Note: For the purpose of Wiki formatting, it was broken up into segments. However, it could have been left as one long string.
	".shiny{ cursor:pointer; border-width:4px; border-style:solid; border-color:#000000; -webkit-border-radius: 18px; -moz-border-radius: 18px; border-radius: 18px; text-align:center;"+
        "height:40px; padding-top:undefinedpx; padding-bottom:undefinedpx; font-size:15px; font-family:arial; color:#ffffff;"+
        "background:#2d10bc; display:inline-block; }.shiny:hover{ background:#e85b4c; }"+
	"</style>");
}

/*CSS For Shiny Button, before it was put into regexplanet as a regular expression, was generated using the "CSS Generator" listed above.
.shiny{
 cursor:pointer;
 border-width:4px;
 border-style:solid;
 border-color:#000000;
 -webkit-border-radius: 18px;
 -moz-border-radius: 18px;
 border-radius: 18px;
 text-align:center;
 width:100px; //Delete the width so it isn't fixed
 height:40px;
 padding-top:undefinedpx;
 padding-bottom:undefinedpx;
 font-size:15px;
 font-family:arial;
 color:#ffffff;
 background:#2d10bc;
 display:inline-block;
 }.shiny:hover{
 background:#e85b4c;
 }
 */
	
	
void main() {
	fields = form_fields();
	success = count(fields) > 0;
	
	page.append("<html><head>"); // write_page()
	styles();
	page.append("</head><body><form name='relayform' method='POST' action=''>");
	write_button_Example("test", "John's Button"); //Will generate a button with just "John" as it uses single quotes in the label instead of \"
	write_button("test2", "John's Button");
	write_button2("test3", "John's Super Button", "shiny");
	write_button2("test4", "John's not so super button");
	write_button2("test5", "John's super awesome shiny button again", "shiny");
	page.append("</form></body></html>"); 	// finish_page()
	writeln(page);
}

Additional CSS Styling Ideas and Jason's functions using page.append can be found in Bale's OCD Relay Script and EoD's Ascension Checklist Relay Script.