Bug - Not A Bug You don't have enough null

stannius

Member
This scriptlet:
Code:
string SubstituteOutShieldbutt(int round, string opp, string text)
{
    string configuredAction = get_ccs_action(round);
    if(configuredAction != "skill shieldbutt" ||
       !have_equipped($item[spooky little girl]))
    {
        return configuredAction;
    }
    
    return "attack with weapon";
}

void main()
{
    adv(1, $location[Domed City Ronaldus Prime], "SubstituteOutShieldbutt");
}

With this CCS:
Code:
[ _olfact ]
"pickpocket"
skill entangling noodles
skill transcendent olfaction
default

[ default ]
try to steal an item
skill entangling noodles
skill stringozzi serpent
skill cannelloni cannon

[ survivor ]
section _olfact

Causes the error message "You don't have enough null" when it hits the "\"pickpocket\""

Is there some way I should be escaping the return from get_ccs_action?
 

slyz

Developer
Could you run this with your CCS and post the results?
PHP:
string SubstituteOutShieldbutt(int round, string opp, string text)
{
    string configuredAction = get_ccs_action(round);
    print( "round " + round );
    print( "configuredAction = " + configuredAction );
    if(configuredAction != "skill shieldbutt" ||
       !have_equipped($item[spooky little girl]))
    {
        return configuredAction;
    }
    
    return "attack with weapon";
}

void main()
{
    adv(1, $location[Domed City Ronaldus Prime], "SubstituteOutShieldbutt");
}
 

stannius

Member
I did print it, though without the "configuredAction =":

CLI output said:
"pickpocket"
You don't have enough null
You're on your own, partner.
Click here to continue in the relay browser.

Round was 0.
 
Last edited:

slyz

Developer
Ah right. You can't return macro commands from filter functions, they have to be regular CCS commands.
 

Veracity

Developer
Staff member
Is there some reason you are using "pickpocket" (the KoL macro verb) rather than KoLmafi'a equivalent - try to steal an item - ?

You are using a filter function which, near as I can tell, is expected to return a single KoLmafia-style CCS line. I have no idea if KoLmafia will macrofy that single line and pass it to KoL as a macro or if it will try to interpret it as a CCS line ( in which case, perhaps it is looking for an item named "pickpocket", hence the "You don't have enough" message) or something else.

Considering that you can make CCS sections complete with KoL macro subroutines and if statements and such, I'm really not sure how a filter function is supposed to interact with a CCS section that contains non-KoLmafia CCS lines...
 

Theraze

Active member
Isn't the standard reason for using "pickpocket" because that will always try regardless of whether or not mafia believes there's any thievable item or reason to do it? I recall that being standard advice... using "pickpocket" instead of try to steal an item... anytime people complain about mafia not stealing when they want it to.

As per these old bug reports:
http://kolmafia.us/showthread.php?4...aren-t-being-macrofied-so-don-t-happen-at-all
http://kolmafia.us/showthread.php?4699-Pick-pocket-stops-working-in-CCS
 
Last edited:

slyz

Developer
Is there some reason you are using "pickpocket" (the KoL macro verb) rather than KoLmafi'a equivalent - try to steal an item - ?
My guess is that stannius is trying to write a combat filter that goes through a user's CCS to prevent the CCS from using shieldbutt during part of the Generator quest where the Spooky Little Girl has to be equipped in the offhand.

This would make combat transparent for the user: he could setup his character to shieldbutt, but the script would prevent errors during the part of the quest where no shield is equipped by replacing shieldbutts by something else.

I came up with this, maybe you could try it out?
PHP:
int lastActionRound ;

boolean isMacroAction( string action )
{
	return
		action.contains_text( "scrollwhendone" ) ||
		action.contains_text( "mark " ) ||
		action.contains_text( "goto " ) ||
		action.contains_text( "if " ) ||
		action.contains_text( "endif" ) ||
		action.contains_text( "while " ) ||
		action.contains_text( "endwhile" ) ||
		action.contains_text( "sub " ) ||
		action.contains_text( "endsub" ) ||
		action.contains_text( "call " ) ||
		action.contains_text( "#" ) ||
		action.contains_text( "\"" );
}

string SubstituteOutShieldbutt(int round, string opp, string text)
{
	if ( round == 0 ) lastActionRound  = 0;
	round = max( round, lastActionRound  );

	string macro;
	while( get_ccs_action( round ).isMacroAction() )
	{
		macro += create_matcher( "\"", get_ccs_action( round ) ).replace_all( "" ) + "; ";
		round += 1;
	}
	if ( macro != "" )
	{
		macro = create_matcher( "skill shi\\w+", macro ).replace_all( "attack" );
		visit_url( "fight.php?action=macro&macrotext=" + url_encode( macro ), true, true );
	}

	lastActionRound = round;
	string configuredAction = get_ccs_action( round );
	if( !configuredAction.contains_text( "skill shi" ) ||
	    !have_equipped($item[spooky little girl]))
	{
		return configuredAction;
	}

	return "attack with weapon";
}

void main()
{
    adventure( 1, $location[ Domed City of Ronaldus ], "SubstituteOutShieldbutt" );
}
 
Last edited:

stannius

Member
Isn't the standard reason for using "pickpocket" because that will always try regardless of whether or not mafia believes there's any thievable item or reason to do it? I recall that being standard advice... using "pickpocket" instead of try to steal an item... anytime people complain about mafia not stealing when they want it to.

My guess is that stannius is trying to write a combat filter that goes through a user's CCS to prevent the CCS from using shieldbutt during part of the Generator quest where the Spooky Little Girl has to be equipped in the offhand.

This would make combat transparent for the user: he could setup his character to shieldbutt, but the script would prevent errors during the part of the quest where no shield is equipped by replacing shieldbutts by something else.

These two read my mind exactly.
 

slyz

Developer
I finally got around to testing this. Here is a version that works:
PHP:
int nextActionRound ;

boolean isMacroAction( string action )
{
	return 
		action.contains_text( "scrollwhendone" ) ||
		action.contains_text( "mark " ) ||
		action.contains_text( "goto " ) ||
		action.contains_text( "if " ) ||
		action.contains_text( "endif" ) ||
		action.contains_text( "while " ) ||
		action.contains_text( "endwhile" ) ||
		action.contains_text( "sub " ) ||
		action.contains_text( "endsub" ) ||
		action.contains_text( "call " ) ||
		action.contains_text( "#" ) ||
		action.contains_text( "\"" );
}

string SubstituteOutShieldbutt( int round, string opp, string text )
{
	if ( round == 0 ) nextActionRound = 0;
	round = max( round, nextActionRound );

	boolean have_shield = $slot[ offhand ].equipped_item().item_type() == "shield";

	string macro;
	while( round.get_ccs_action().isMacroAction() )
	{
		macro += create_matcher( "\"", round.get_ccs_action() ).replace_all( "" ) + "; ";
		round += 1;
	}
	if ( macro != "" )
	{
		if ( !have_shield )
		{
			macro = create_matcher( "skill shi\\w+", macro ).replace_all( "attack" );
		}
		visit_url( "fight.php?action=macro&macrotext=" + macro.url_encode(), true, true );
	}

	string action = round.get_ccs_action();
	if( action.contains_text( "skill shi" ) && !have_shield )
	{
		action = "attack with weapon";
	}

	nextActionRound = round + 1;
	return action;
}
 
Last edited:

stannius

Member
Thanks slyz. I have incorporated your code into my script. I did add a little something, since the goal is to kill scaling monsters (and "attack with weapon" may not succeed)
Code:
/// <summary>
/// Find an appropriate skill with which the character can kill scaled monsters
/// </summary>
/// <param name="includeShieldbutt">if false, filter out shieldbutt</param>
string GetKillingWord(boolean includeShieldbutt)
{
    foreach skill in $skills[shieldbutt, stringozzi serpent, saucegeyser, spectral snapper, lunging thrust-smack, clobber, toss]
    {
        if(have_skill(skill))
        {
            if(!includeShieldbutt && skill == $skill[Shieldbutt])
            {
                print("shieldbutt won't work without a shield.");
                continue;
            }

            print("using skill " + to_string(skill));
            return "skill " + to_string(skill);
        }
    }

    print("no suitable battle action found. attempting a simple attack.");
    return "attack with weapon";
}
 

slyz

Developer
This code does submit the CCS actions one by one though. The best course would be to completely reproduce Mafia's "turn CCS into macro" capability, to turn the whole CCS into a macro in one go before replacing shieldbutt, but that's a bit of extra work.
 

stannius

Member
This code does submit the CCS actions one by one though. The best course would be to completely reproduce Mafia's "turn CCS into macro" capability, to turn the whole CCS into a macro in one go before replacing shieldbutt, but that's a bit of extra work.

I'll work on that for the next version.
 
Top