Feature - Implemented Custom combat disable for fully scripted KoLMafia users

katyarn

Member
There's a growing prevalence of scripts using either KoL's autoattack system, or passing in macro strings or functions to adv1 and runCombat. Personally I'm a big fan of the design to passing in a macro to adv1/runCombat since it lets you keep all combat code in one logical grouping there's no surprise how combat functions get called back from KoLmafia.

The big consequence of this design is that all the scripts using this system want to set KoLmafia's builtin CCS to do nothing. The solution right now to do that is to distribute your own empty CCS file, with a line like scrollwhendone or twiddle. This is not really a problem now, but I don't think it's a very good scalable solution. Users will find themselves with an ever growing list of CCS files that do nothing as they download these projects.

While I think KoLmafia will generate its own default.ccs file, I imagine a lot of users are accustomed to actually modifying it when writing their own combat macros so relying on that isn't a viable solution. Instead, I propose that KoLmafia that needs a preference to disable the builtin custom combat system and graphically show the user it is disabled when they look at that tab.
 

MCroft

Developer
Staff member
There's a growing prevalence of scripts using either KoL's autoattack system, or passing in macro strings or functions to adv1 and runCombat.
[...]
I propose that KoLmafia that needs a preference to disable the builtin custom combat system and graphically show the user it is disabled when they look at that tab.
Would it make sense to selectively disable CCS when passing in a macro to those functions? If you don't have it, go ahead and CCS. If you do have it, it's like a command line override.

If we wanted to be even more explicit, we could have a version of those commands that has an extra boolean parameter for bypassing CCS.
 

gausie

D̰͕̝͚̤̥̙̐̇̑͗̒e͍͔͎͈͔ͥ̉̔̅́̈l̠̪̜͓̲ͧ̍̈́͛v̻̾ͤe͗̃ͥ̐̊ͬp̔͒ͪ
Staff member
Would it make sense to let users include the statement disableccs; or something to their macro which we can read out and follow?
 

Malibu Stacey

Active member
Would it make sense to let users include the statement disableccs; or something to their macro which we can read out and follow?
Problem there is if you do something like
Code:
use(1, $item[photocopied monster]);
It will still call your CCS to handle the fight as it won't have got to your
Code:
run_combat("your macro here");
call yet.

Also note I am not the target demographic for this change. My default.ccs looks like
Code:
[ default ]
consult oracle.ash
So I can handle all combat in my consult script. This however was the best solution many years ago which handled all types of combats which is no longer the case with all the exceptional work done to improve scripting in the last year.

autoscend however is one of those scripts which supplies an "empty" .ccs file as Katarn mentions above (it simply contains a "scrollwhendone") as it uses combat filter functions in ASH for all combat handling.
 

katyarn

Member
I should have actually listed some of the edge cases that scripts handle currently:
  • Using an item that leads to a fight (BRICKOs, fax, desk bells, Seal Clubber figurines, etc.)
  • Casting a skill that leads to a fight (Evoke Eldritch Horror)
  • Entering a fight via a choice adventure that isn't in a zone (science tent, Witchess)
  • Entering a fight via a URL that isn't any of the above (Chateau painting, God Lobster challenge)

Setting a KoL autoattack solves most of these, unless the enemy is a boss (such as God Lobster) and you lose initiative. In that case, KoL's built-in autoattack won't trigger and KoLmafia will run CCS. That said, I don't think setting autoattacks for every fight is a perfect solution to avoid CCS, sometimes you want to react to events in combat with different decisions that BALLS macros can't handle.

Would it make sense to selectively disable CCS when passing in a macro to those functions?
This covers some cases, I think it'd actually be a good thing in general to do that although it could be a breaking change for some scripts that depend on the current behavior (I hope not but, it's possible). Although I haven't experienced it, I imagine that passing in a callback to adv1 and having CCS trigger is highly undesirable.

The other case of using an item or visiting a URL and then calling runCombat needs some other handling. I think adding an additional boolean to all the various ways of entering combat wouldn't really make sense, especially for visitUrl which has two optional booleans already.
 
Last edited:

gausie

D̰͕̝͚̤̥̙̐̇̑͗̒e͍͔͎͈͔ͥ̉̔̅́̈l̠̪̜͓̲ͧ̍̈́͛v̻̾ͤe͗̃ͥ̐̊ͬp̔͒ͪ
Staff member
I'd be happy for now to make an inbuilt noop CCS
 

katyarn

Member
One more quick thought: Having an API to switch to a temporary disabled CCS could solve things cleanly.
So a code solution in js might look something roughly like:

JavaScript:
const oldCCS = getProperty('customCombatScript');
const ccsName = `${projectName}Temp.ccs`
const ccsData =
'[ default ]\
scrollwhendone';
try {
    setCCS(ccsName,ccsData);
    restOfScript();
} finally {
    removeCCS(ccsName);
    setProperty('customCombatScript', oldCCS);
}
 
I encountered this recently as I've started dabbling in TS. The blank CCS seemed like a strange thing to me and I tried avoiding it.

Would it make sense to move any sort of runCombat() logic ahead of trying to fetch a CCS/consult?
 
Last edited:
Even if that happened, you'd still want a blank CCS.
Otherwise, it will still take over when your intended combat handling fails for whatever reason.
 
Apparently, some of you expect to be able to finish off the monster in your functions. And sometimes, you fail? And you are not willing to let the user-created default CCS finish it off for you? Because, ... why?

What do you want to happen?
The scripts I maintain do a wide variety of things during combat. They kill things, they run away from things, they saber things, they copy things.
Something that has happened very often to new users of garbo is that they will have a user defined CCS that says something like "attack, repeat", and for some reason garbo fails to disable that CCS (or they have a misconfigured CCS which will interpret itself as attac, repeat). One thing that garbo will do is attempt to free run away from some monsters, like crates in Noob cave in order to set up combat that allows for the use of replacing skills (Macrometeorite, REPLACE ENEMY) into a Knob Goblin Embezzler using the "Be Gregarious" skill. If a user has a custom combat script enabled, sometimes the script will just repeatedly fight the crates, hit them, and waste a turn.

The entire ethos of the script is that "garbo knows best". It is supposed to be minimally configurable - everything is supposed to work out of the box, but also you cannot configure it to act in any nonstandard way because it is a very delicate script. I never want to use a users CCS, because garbo has a very particular set of behaviors it is trying to use. As the script developer I would much rather the script abort after combat ("Your own your own, partner") than attempt to use a combat script that will waste thousands of meat and possibly hundreds of turns.

As someone who has started using more and more of these "delicate" scripts, I just went through my CCS and found that I have 5 that are variants of "consult a script and then abort" or "scrollwhendone" or "twiddle", all from different scripts that use similar combat engines.

What I believe this is asking for should also not affect other scripts - all I would like to see is a way to optionally turn off CCS, or a default CCS that does nothing so that not every script maker has to make their own CCS to accomplish the same thing.
 

gausie

D̰͕̝͚̤̥̙̐̇̑͗̒e͍͔͎͈͔ͥ̉̔̅́̈l̠̪̜͓̲ͧ̍̈́͛v̻̾ͤe͗̃ͥ̐̊ͬp̔͒ͪ
Staff member
@Veracity my understanding of the requirement is as follows:

Your script is running a series of fights in order by using copies. Imagine you want to submit a different macro for every fight.

Because the "use" function does not accept a macro (maybe it should?) you might want to use the copy item, have your CCS do nothing and then "run_combat" with your chosen macro. To many this is preferable to having interreliant logic between your main script and your combat script to work out which macro to run when.

I think that requirement is fine and we should be able to support it in mafia, either by letting "use" take a macro in case the consumption results in a combat (maybe this is nice, though it doesn't cover other fight starters like the genie or reminisce command) or by adding a built-in "do nothing" CCS that scripts can just switch to instead of all shipping their own
 

gausie

D̰͕̝͚̤̥̙̐̇̑͗̒e͍͔͎͈͔ͥ̉̔̅́̈l̠̪̜͓̲ͧ̍̈́͛v̻̾ͤe͗̃ͥ̐̊ͬp̔͒ͪ
Staff member
What I believe this is asking for should also not affect other scripts - all I would like to see is a way to optionally turn off CCS, or a default CCS that does nothing so that not every script maker has to make their own CCS to accomplish the same thing.
This is what I'm thinking yes
 

Ryo_Sangnoir

Developer
Staff member
A way to turn off running a CCS globally would be nice (preference-based?). I have a CCS that just aborts the combat which I use in-run when running e.g. "numberology 51", because I want to be dumped in the combat with the Frat Soldier instead of having Mafia take any actions at all.

A CCS that does literally nothing would be equivalent, I think.
 

gausie

D̰͕̝͚̤̥̙̐̇̑͗̒e͍͔͎͈͔ͥ̉̔̅́̈l̠̪̜͓̲ͧ̍̈́͛v̻̾ͤe͗̃ͥ̐̊ͬp̔͒ͪ
Staff member
A way to turn off running a CCS globally would be nice (preference-based?). I have a CCS that just aborts the combat which I use in-run when running e.g. "numberology 51", because I want to be dumped in the combat with the Frat Soldier instead of having Mafia take any actions at all.

A CCS that does literally nothing would be equivalent, I think.
I was looking at the code and its more likely that we want a new setting for the battleAction pref to make this work
 

ereinion

Member
My ccs has one action which is "abort", which I think does nothing? Or is it something else you guys are looking for?
 
Apparently, some of you expect to be able to finish off the monster in your functions. And sometimes, you fail? And you are not willing to let the user-created default CCS finish it off for you? Because, ... why?
Because if the automation I put in DIDN'T kill the monster, something went wrong (I probably fucked up somewhere), and I want to know so I can go and fix it.
I don't want mafia to continue, silently suppressing the error and possibly ruining whatever I was trying to do in that combat that now failed.

The thing is that this is fairly common assumption script writers want to make, but since there's no default 'do nothing' ccs option (mafia actively tries to prevent you from setting one, defaulting to 'attack with weapon') each script has to include their own.
I am completely unwilling to change the code to require my script to, somehow, have to figure out out to finish off a monster, other than punting to the CCS, which I have specifically configured to do that for me.

Work with the tools we provide you - which are ample - rather than proposing a change which will break scripts which are already happily using those tools.

We aren't asking you to break your scripts. We are asking for a change in mafia so that we can break our own scripts in the way we want them to. Because the failure case of 'this combat didn't resolve the way we expected it to, figure it out yourself and/or report it to whoever made the script' is more obvious and less harmful than 'this combat didn't resolve the way we expected it to, so I'm just gonna waste 348 adventures trying to see if that goes any better'. For dumb cases like "lets go banish monster X, but oops, our outfit forgot to include a banisher".

And it's not just failure cases. For 'weird' ways to start a combat like bricko fights or genie wishes, you don't always get a chance to include your own combat handling as part of starting the fight, which means mafia will activate your ccs before you can follow up with a run_combat("What I actually want");

I'm sure custom combat scripts are great if you tell them to do what you want them to do. But I want them to do nothing. And it's surprisingly hard to tell them that, because mafia tries to be helpful and prevent you from locking yourself into a combat with 'missing' combat handling. (Setting your battleAction to "" or trying to make an empty consult script both times makes it default to "attack with weapon")

The workaround historically has been "ship your script with your very own ccs that does nothing, and hope other users don't edit it". Which works, but if you have a bunch of those scripts, you end up with a very bloated list that yet has nothing in it.

The change could be something like "if you set your battleAction to 'script' or 'twiddle' or 'consult', mafia doesn't use your custom combat script at all, even if your kolnative autoattack or consult script failed" or "mafia now includes a twiddle.ccs, that purposefully does nothing and can't be changed, so that scripts can switch to it and assume it does what they want it to (which is nothing)"
 
Last edited:

fronobulax

Developer
Staff member
The entire ethos of the script is that "garbo knows best". It is supposed to be minimally configurable - everything is supposed to work out of the box, but also you cannot configure it to act in any nonstandard way because it is a very delicate script. I never want to use a users CCS, because garbo has a very particular set of behaviors it is trying to use. As the script developer I would much rather the script abort after combat ("Your own your own, partner") than attempt to use a combat script that will waste thousands of meat and possibly hundreds of turns.

As a script USER if the script advertises it knows best and then aborts after combat that to me is a script problem. The script clearly does not know best or know enough. As a USER I would expect the script author to fix their script or amend their advertising. If the solution is for the script author to relax a "minimal configuration" goal/requirement then that's fine. As a USER I have seen solutions that work well - the script saves my state, replaces my CCS etc. with theirs, and restores things when the script is done. The good scripts use a try/catch/finally construct to guarantee restoration and have checks in place to try and detect a catastrophic restoration failure. My recollection is somewhere in the autoascend forks this was done quite satisfactorily.

Since one of my philosophies for adding features to mafia includes asking whether a script could accomplish the same thing, it seems like this is more of a convenience than a necessity.

Since "minimal configuration" is a goal I have to ask whether making this preference-based makes a script minimally configured at the cost of increasing the complexity of mafia configuration? If this feature is behind an opt-in preference the burden is now on the user to opt-in when running running some scripts but opt-out before running others. I'm sure a user's failure to opt-out would also have the potential to burn thousands of meat and hundreds of turns and the blame could clearly, if unfairly, be laid on the user and not script authors.
 

gausie

D̰͕̝͚̤̥̙̐̇̑͗̒e͍͔͎͈͔ͥ̉̔̅́̈l̠̪̜͓̲ͧ̍̈́͛v̻̾ͤe͗̃ͥ̐̊ͬp̔͒ͪ
Staff member
As a script USER if the script advertises it knows best and then aborts after combat that to me is a script problem. The script clearly does not know best or know enough. As a USER I would expect the script author to fix their script or amend their advertising. If the solution is for the script author to relax a "minimal configuration" goal/requirement then that's fine. As a USER I have seen solutions that work well - the script saves my state, replaces my CCS etc. with theirs, and restores things when the script is done. The good scripts use a try/catch/finally construct to guarantee restoration and have checks in place to try and detect a catastrophic restoration failure. My recollection is somewhere in the autoascend forks this was done quite satisfactorily.
garbage-collector, for example, relies on replicating the behaviour described in this feature request to do exactly what you're suggesting
Since one of my philosophies for adding features to mafia includes asking whether a script could accomplish the same thing, it seems like this is more of a convenience than a necessity.

Yes this is a convenience. It doesn't really affect script authors though, its a convenience for script users to not have loads of useless empty ccs files in their list.

Since "minimal configuration" is a goal I have to ask whether making this preference-based makes a script minimally configured at the cost of increasing the complexity of mafia configuration? If this feature is behind an opt-in preference the burden is now on the user to opt-in when running running some scripts but opt-out before running others. I'm sure a user's failure to opt-out would also have the potential to burn thousands of meat and hundreds of turns and the blame could clearly, if unfairly, be laid on the user and not script authors.
As a USER what would you prefer frono?
 
Top