Scripting semi-rares

Linknoid

Member
I want my script to run a semi-rare automatically whenever the counter expires. Strangely enough, it works fine for the purple light district, but when it tries to run the castle top floor, it stops the world "Fortune Cookie counter expired." so the user can decide where to spend the adventure.

I want to be warned when I'm playing and the semi-rare counter comes up. But I expect my script to be able to take care of it for me while it's running, without manual intervention.

Here's some minimal reproducible code to demonstrate the issue:

Code:
boolean IsPurpleLightAvailable()
{
    matcher imgNum = create_matcher("purplelightdistrict(\\d+)\\.gif", visit_url("clan_hobopolis.php?place=8"));
    return imgNum.find() && imgNum.group(1).to_int() < 10;
}

string runaway(int round, monster mon, string page)
{
    return "run away";
}

void RunSemiRare()
{
    string lastLocation = get_property("semirareLocation");
    if (lastLocation != "The Castle in the Clouds in the Sky (Top Floor)")
    {
        visit_url("adventure.php?snarfblat=324"); // castle top floor
        run_combat("runaway");
    }
    else if (IsPurpleLightAvailable() && "hobo nickel".to_item().item_amount() >= 5)
    {
        string page = visit_url("adventure.php?snarfblat=172"); // purple light district
        if (page.contains_text("choice.php"))
            run_choice(1);
        else
            run_combat("runaway");
    }
    else
    {
        // do something else, irrelevant to this question
    }
}

void main()
{
    string countersString = get_property("relayCounters");
    string[int] counters = countersString.split_string(":");
    int fortuneCookieCounter = 0;
    for (int i = 2; i < counters.count(); i += 3)
    {
        int turns = counters[i - 2].to_int();
        string type = counters[i - 1];
        if (type.index_of("Fortune Cookie") >= 0)
        {
            if (fortuneCookieCounter == 0 || turns < fortuneCookieCounter)
                fortuneCookieCounter = turns;
        }
    }
    print("turns until semi-rare " + (fortuneCookieCounter - my_turnCount()));
    for (int i = my_turnCount(); i < fortuneCookieCounter; i++) // burn turns until semi-rare is up
    {
        visit_url("adventure.php?snarfblat=83"); // visit the hole in the sky, because it doesn't have any choice adventures to interfere
        run_combat();
    }
    RunSemiRare();
}
 

Linknoid

Member
Based on this discussion http://kolmafia.us/showthread.php?22092-cli_execute-can-cause-an-unskippable-error-Propose-try-catch, the following code can be used to clear the semi-rare warning without killing the script:

Code:
boolean ignore = cli_execute("try; visit_url adventure.php?snarfblat=99999999");

I tested this in the above test code several times, and it sends a red "Fortune Cookie counter expired." message and then continues running the script as normal.


Explanation of why it works:

Mafia keeps track in memory if it has ever warned about each timer, so it will only warn once for each timer during a given session. If it hasn't warned you yet, when you visit a URL which might spend an adventure, it sends an abort. That abort shows up as a warning screen for the user, but in a script, it kills the whole thing.

I traced through the code and couldn't see any reason why it wouldn't send the abort for the purple light district, so I kind of put it on hold. And with no "catch" mechanism, I couldn't figure out how to properly continue my script after a known abort event.

But the demo of how to use the try statement from cli_execute is just what I needed. The try statement is one of the only things that actually resets the abort status (whereas ash try/finally only resets it temporarily). So if you visit an invalid URL which looks like it will take an adventure, it will trigger the semi-rare warning, and the try will eat the abort. Then the script can continue handling the semi-rare.
 

Linknoid

Member
I can understand people wanting a counter script. However, I generally like to have manual control over my semi-rares. I like getting the warning and getting a chance to decide... unless I'm running a script that I want to run without interruption.

The goal is, if there's a counter script installed, let it have first dibs, but handle the semi-rare in my script if there isn't. That should happen naturally with my "try; ...9999999" solution, right?
 
Top