OCD Inventory control

I ran into a bug. I had 2 items I already categorized but I wanted to change to gift. 1 was under autosell, the other was under keep. When i changed them to send as gift under the relay interface, they both disappeared from the relay lists and were UNKN in the data file. I had to change them both to gift before i could use the relay interface to pick a person to send them to.
 
After thinking about this I finally realized what happened. A gift without a sender is considered user error and so they got marked as UNKN. Then when you marked them as gift in the "add items" interface you were able to put in the sender so they stuck. I can see how that was confusing since there is no place to add a recipient in lists of items to autosell or keep. It is certainly a lack in the UI.

To fix this properly I think I'd need to learn how to integrate javascript better so that a place for a recipient open up automatically when you change the drop-down to "gift". That's probably not going to happen soon.
 
Has anyone suggested an "untinker" option? If so, consider it re-suggested. I've rigged mine to display PA info for each item and there are several cases where it would be nice.
 
Well, untinkering didn't seem like too difficult an addition and it won't clutter the interface much, so I did it for you despite the fact it seems unlikely to be interesting to many people.

Now includes untinkering support:
* Bale's OCD Inventory Control version 3.35
* relay OCD dB Manager version 1.35

I hope that helps.
 
Wow, swift! Thanks Bale. I'm using this script now for a multi and loving it! Will probably make the transition soon to use this on my main as well.

I haven't checked, but does this consider casting Inigo's before crafting items that will cost adventures? Might be a nice addition if it doesn't.
 
No this does not consider Inigos before crafting. I agree that is nice, but I don't think it really fits for this script. This script is more about what to do when you have items, regardless of having casts of Inigo left today. People trying to use up Inigo are more interested in making us of their Inigo with stuff that is high value to craft, even if they have to purchase stuff to make it. Those objective sets don't combine easily enough for me to just automatically cast it. What if you only need 5 turns of Inigo and it casts Inigo for 10 turns. You'd actually end up wasting a turn of Inigo unless it only cast Inigo if it exactly fit the amount you need and that gets complicated because I'd need to differentiate between crafting that uses Inigo and crafting that does not use Inigo.

In short, it would not only be difficult, but also likely to cause annoyance for certain play-styles.

PS. I'm very happy you're loving this script.
 
Just balance the MP cost of casting Inigo's vs. the cost of adventures spent, and only cast it if it's profitable and you have a song slot available. I can't imagine that making anyone upset, since they can still spend their adventures more profitably than if they had spent them crafting, even if a few turns of Inigo's go to waste. Don't worry about making slots available or buying and using recordings -- that can be up to the user to handle, but if you have the skill and an available song slot, especially for large inventories when this is running for the first time, it could be a very nice feature.

In case it was the "complicated" part that is disinclining you, I wrote up a function to tell you how many turns of Inigo are required to craft a given item:

PHP:
record howtomake {
   string method;
   string blah;
};
howtomake[item] making;
int inigo_required(item it) {
   if (count(making) == 0 && !file_to_map("concoctions.txt",making)) vprint("Unable to load concoctions.txt.",0);
   if (!(making contains it)) return 0;
   if (contains_text(making[it].method,",")) making[it].method = excise(making[it].method,"",",");
   switch (making[it].method) {
     // consumes adventure (bartender)
      case "ACOCK":
      case "SCOCK":
      case "SACOCK":
      case "MIX": return to_int(!have_bartender())*5;
     // consumes adventure (chef)
      case "PASTA":
      case "TEMPURA":
      case "SAUCE":
      case "SSAUCE":
      case "DSAUCE":
      case "COOK": return to_int(!have_chef())*5;
      case "WOK": return 5;
     // consumes adventure (innabox)
      case "SMITH": if (in_muscle_sign()) break;
      case "WSMITH":
      case "ASMITH":
      case "SSMITH": return 5;
     // consumes adventures! (jewelry)
      case "EJEWEL":
      case "JEWEL": return 15;
   }
   return 0;
}

From here, it's pretty easy to determine based on your accordion (and let's not forget the JEW hat) how many casts of Inigo you'd need to make n X's, then take n*mp_cost(inigo's)*_meatpermp and if it's less than inigo_required()*valueOfAdventure/5, go for it!

If you're still disinclined, I'll just quietly add this to my own version and say no more.
 
Your function fails. Bar whip costs 5 turns of Inigo, Chrome Sword costs 10 turns of Inigo and hairy staff costs 15 turns of Inigo. And how about spooky stir-fry? It's just basic cooking, but secret blend of herbs and spices is a fancy ingredient, so it costs 5 turns of Inigo. (And this one was a simple error, but supercocktails cost 10 turns of Inigo, not 5.) Sorry, I stand by my statement that Inigo calculation is complicated.

As for your other point, you are ignoring the fact that Inigo is limited to 5 casts per day. That means it has an opportunity cost, not just a cost/adventure. How do you calculate the opportunity cost?

The problem is not trivial.
 
Last edited:
Eh?? Evidently my function has not been updated properly since cooking/crafting was reworked -- this was part of an Eat/Drink rewrite I was working on before the revamp, but it returned the total cost of the creation method (10 for pasting, valueOfAdventure*number of adventures required, etc). In this case it should also return 0 for WOK, not 5, since Inigo's doesn't work there. I'm still behind on all the changes that have happened to KoL in the last several months -- it's kind of been a blur.

Seriously -- started work on BatMan, and KoL broke many of my other scripts by releasing the rest of the nemesis quest. Got those mostly fixed and started to work again on BatMan and then KoL released macros, which meant a major reworking of what I had so far to make predictive action trees. Got an Eat/Drink rewrite (Ingest/Imbibe) mostly operational (and perhaps more importantly, a predictive creation cost script), and KoL thoroughly reworked cooking. I know it's not, but sometimes it feels ... deliberate. I sometimes think I can control what they update next by choosing to devote a lot of hours to scripting something for it. [/whining and self-pity]

But this particular function is not as broken as you made it sound. I did mention that it does not account for crafting sub-ingredients. For this script, so long as users only specify creation of the next tier up (in your example, chrome ores would be made into chrome meat stacks, which would separately be made into chrome swords), it's mostly fine. It only needs to account for fancy ingredients to be functional.

Opportunity costs for anything scripted should pretty much always be settings -- in this case, perhaps use_inigo (boolean or always|aftercore|never) would do the trick.

Anyway,

tl,dr: I will quietly add Inigo's support to my local copy and not take any more of your time. :)
 
This is the function I use in my Familiar Feeder do determine if crafting something will take an adventure. Most of it was stolen from Price Advisor.
PHP:
string[item] concoctions ;	  // stolen from PriceAdvisor.ash
boolean[item] currently_considering; // prevent infinite recursion

if ( !file_to_map("concoctions.txt", concoctions) )
	abort("Failed to load concoctions.txt");

// Add funny concoctions that don't load properly
concoctions[$item[bottle of gin]] = "MIX";
concoctions[$item[bottle of rum]] = "MIX";
concoctions[$item[bottle of sake]] = "MIX";
concoctions[$item[bottle of tequila]] = "MIX";
concoctions[$item[bottle of whiskey]] = "MIX";
concoctions[$item[bottle of vodka]] = "MIX";
concoctions[$item[boxed wine]] = "MIX";	

// strip extra conditions used by Mafia -- 
// taken into account by get_ingredients(), so I don't need 'em
foreach itm, c_type in concoctions {
	c_type = replace_string(c_type, "TORSO", "");
	c_type = replace_string(c_type, "FEMALE", "");
	c_type = replace_string(c_type, "MALE", "");
	c_type = replace_string(c_type, "HAMMER", "");
	c_type = replace_string(c_type, "WEAPON", "");
	c_type = replace_string(c_type, "SSPD", "");
	c_type = replace_string(c_type, "GRIMACITE", "");
	c_type = replace_string(c_type, "SX3", "");
	
	// remove any remaining trailing ", "
	while (last_index_of(c_type, ", ") == length(c_type) - 2)
	{ c_type = substring(c_type, 0, length(c_type) - 2); }
	
	concoctions[itm] = c_type ;
}

boolean uses_advs( item itm ) {
	boolean uses_adv = false ;
	if ( is_npc_item(itm) ) return false ;
	if ( count(get_ingredients(itm)) == 0 ) return false ;
	
	switch ( concoctions[itm] ) {
		// always uses an adventure to make
		case "WSMITH":
		case "ASMITH":
		case "MALUS":
		case "WOK":
		case "MSTILL": 	// make sure the still won't be used
		case "BSTILL": 	//
		case "JEWEL":
		case "EJEWEL":
		case "EXPENSIVE":
			return true ;
		// never uses an adventure to make
		case "PIXEL":
		case "STAR":
		case "ROLL":
		case "SUSE":
		case "MIX":
		case "COOK":
			return false ;
		// sometimes uses an adventure to make
		case "SMITH":
			uses_adv = !in_muscle_sign() ;
			break ;
		case "MIX_FANCY":
		case "ACOCK":
		case "SCOCK":
		case "SACOCK":
			uses_adv = !( get_campground() contains $item[bartender-in-the-box] || get_campground() contains $item[clockwork bartender-in-the-box] ) ;
			break ;
		case "COOK_FANCY":
		case "PASTA":
		case "SAUCE":
		case "SSAUCE":
		case "DSAUCE":
		case "TEMPURA":
			uses_adv = !( get_campground() contains $item[chef-in-the-box] || get_campground() contains $item[clockwork chef-in-the-box] ) ;
			break ;
	}
	
	currently_considering[itm] = true ;	
	if ( uses_adv == false )
		foreach ingred in get_ingredients(itm) {
			if ( !currently_considering[ingred]  && uses_advs(ingred) )
				return true ;
	}
	remove currently_considering[itm] ;
	return uses_adv ;
}

</threadjack>
 
Last edited:
Yeah, see, MIX_FANCY and COOK_FANCY did not exist back when I was working on Ingest/Imbibe.

This still doesn't return how many adventures a multi-tiered creation will take, but since it recurses ingredients it's a step closer.

You could integrate a few things from mine -- you could be making use of have_bartender() and have_chef(), and stripping the method down to its first comma is much simpler than specifically removing strings.
 
Feature request: List of items to exclude from all OCD actions (and a checkbox to toggle on/off said exclusion). In this holiday season it's irritating to have to keep taking things out of my DC (as I need to run Inventory Control every day to ease my OCD). I could change all the relevant items' actions, but that would be tedious.
 
Just a thought -- but perhaps OCD could be reworked so that rather than having data files for each character, each character would simply have a preference specifying which OCD data file to use.

In addition to solving Banana's problem above, this would allow for multi-pass strategies (use one data file the first time, and a different data file the second time). It would also allow for separate in-run vs. pre-ascension data files. It would also mean a more modular approach, meaning that data files could be shared more easily.
 
To fix this properly I think I'd need to learn how to integrate javascript better so that a place for a recipient open up automatically when you change the drop-down to "gift". That's probably not going to happen soon.

No need; just add attr("onchange='document.relayform.submit()'") before the call to write_select(), and your form will reload. You can just add checking-code in to the script.
 
zarqon: That's actually an interesting idea because it would allow multiple characters to use the same OCD data easily. I'll consider that.

No need; just add attr("onchange='document.relayform.submit()'") before the call to write_select(), and your form will reload. You can just add checking-code in to the script.

So, if I do that the entire form will reload as soon as someone selects something from the drop down?
 
So, if I do that the entire form will reload as soon as someone selects something from the drop down?

Yup, but there's no associated button action that will test true. It works well for me, but I seem to have an odd way of writing relay info scripts, so YMMV. ;) But it's one of those things that jasonharper thought would be useful enough that he included it in the HTMLform summary.
 
Seeing as it can take a minute or two to load the page with enough items in it in FF I'd recommend setting that as a option somewhere...
 
Seeing as it can take a minute or two to load the page with enough items in it in FF I'd recommend setting that as a option somewhere...

I would go so far as to say that this script is incompatible with firefox anyway. When I tell people about it, I tell them they need to open up another web browser to view the configuration page.
 
I've had no problem using it in FF after the first page load which can take some time. But after that there seems to be no problem.
 
Back
Top