Artisan Worktables 1.12

Artisan Worktables 1.12

3M Downloads

[Suggestion] Recipe type for Weighted Exclusive outputs

thephoenixlodge opened this issue ยท 14 comments

commented

Something a bit different I'd love to be able to do would be the ability to have a recipe have different possible outputs based on weightings. This would be different from the current system of different outputs in that you'd only get one of the possible items, rather than the specific one guaranteed with individual chances to also get each extra one.

This would be particularly useful for me working on the RPG-ish Sprout Succession. An example of how I would make use of this would be crafting equipment and having different weightings for the output to come with different bonuses. Eg a chance for a chestplate to come with either a health boost effect, or a curse of binding.

Similarly I would use it for weighted quality/rarity of output items, for example a recipe which turns wool into a cloth item in the tailor's table, normally simply getting normal quality cloth, but with a chance of getting high quality or low quality cloth items instead(with each cloth having different values when sold to NPCs). To then take this a step further I could use the extra wool types from Animania to have different recipes with slightly different weightings for the different qualities (for example maybe a variant where there's reduced chance for low quality cloth, but also no chance for high quality)

commented

I like the idea and I can definitely see how it could be useful. You gave some pretty clear use cases.

I'm going to have to think about how to implement this in the best way.

It would probably use a new CT ZenMethod.

Internally, it would sequentially roll against the extra output chances to see if the item being crafted is replaced. For example, if the first roll failed, it would roll against the second one, and so on. If all of the rolls failed, the default recipe result would be given. So you could define cloth, uncommon cloth (10%), rare cloth (1%). The percentile math probably wouldn't be exactly what the recipe designer intends though because in the example, there is actually a 0.9% chance to get the rare cloth, not the listed 1% (0.9*0.01=0.009). The first roll must fail for any chance to succeed on the second roll.

I could implement a weighted picking system instead. The CT recipe syntax wouldn't take float chances for each extra item listed. It would instead take integers which could be converted to floats internally to make use of the existing recipe code. If using weights, a base weight for the base item would also need to be defined. That could be defined in the ZenMethod by the recipe designer or hard-coded... not sure which way would be better.

When crafting the recipe, the player would see the base item in the result slot. When they take the item, the system would choose a result from the weighted picker and replace the item they just picked up with the new item. For example, the player would see common cloth in the result slot. When they pick it up, it changes to rare cloth.

Displaying these recipes to the player in JEI poses a different problem. They need to be presented in a way that distinguishes them from the existing 'additional output' type recipes that, I think, most players are used to (ie. Thermal Expansion machines, etc.). They would still need to display the weights to the player in a way that makes sense. Percentages are pretty easy to understand, while weights aren't as straightforward. I can look at 10% 15% and 75% and understand easier than, say 2, 3, 15. To solve this, I guess the weights provided in the recipe could be translated to percentages when displayed to the player.

I need to give this more thought. I am open to suggestions.

commented

I think the weighted picking system would be much better. It's a pretty widely used concept throughout MC, so most pack devs should be at least trying to understand it. The problem you described with the first option being not necessarily what the pack dev intends rates to be would also carry over to the user viewing the recipe in JEI, which would only serve to confuse things. Never mind that explaining it working as rerolls to pack devs and users alike could get confusing
As far as displaying the recipes in JEI goes, I definitely think transforming the weighted values to % would be best, and it's a pretty simple calculation (=100*(weight/(sum(weights)))). Would it be possible to have the primary output slot just cycle through the options the way oredict inputs do? I think that'd be the easiest way to make sure it's clear that it's not chances for extras like the other recipe types.

commented

I agree that weighted picking is the best option.

A friend and I came to the same conclusion you did regarding cycling the output slot similar to oredict inputs. I'm pretty sure JEI allows specifying a list of outputs. I just double-checked and I'm confident that I can get the output slot to cycle fairly easily.

JEI also allows manipulation of the tooltip provided for its slots. I'm thinking that, in displaying these types of recipes, the three output slots to the right of the main output should remain empty. Instead we can cycle the main output with the (up to four) weighted outputs and display the calculated percentage to the player via the tooltip.

How does this sound to you?

commented

Sounds great. Could even take it a step further later and allow extra outputs to be a thing with the variable first output too (combining both recipe types) but that's something to think about after getting the initial version working in the first place

commented

allow extra outputs to be a thing with the variable first output too

Could you elaborate on this, please? I'm not sure I quite understand what you mean.

commented

I mean to also have an option to combine the two recipe types - different possible primary outputs, while also having the chances for extra outputs

commented

Ah yes, I understand now. Thanks for the clarification. I like to know ahead of time where I'm going with features so I can plan code changes accordingly.

You can expect the discussed feature in the next feature build. :)

commented

I look forward to it eagerly :D

commented

Everything has been implemented as discussed here and in #22. The recipe back-end has been refactored, the JEI plugin has been updated to handle the new recipe output, and the back-end recipe builder has been exposed through ZenScript.

Much of the work has been done and all that remains is:

  • Creating documentation
  • Resolving an item dupe bug when shift-click crafting (the tip of an interesting problem)

The current code really only handles the case in which a player simply clicks in the crafting slot to pick up the result. If the player shift-clicks in the slot, the displayed result will be placed in their inventory and the weight-picked result, the result they should have been given, will be placed into their cursor.

I'm also pretty sure that the current code for replacing the weighted item during the craft will break with a mod like MouseTweaks. If a recipe has an output with a quantity > 1, let's say 5x cobble, when the player scrolls the mouse wheel to take one item from the crafting result slot, one item from the displayed stack will be placed in their inventory and the actual weight-picked item stack will be placed into their cursor. I haven't tested that this behavior occurs, but I anticipate that it does.

One solution to this would be to limit the quantity of the resulting base item of any multi-output, weight-picked recipe to one.

After looking at MouseTweaks again, I discovered that they have an API that allows customization of the mod's behavior per gui container. This is obviously the best approach for this specific problem.

A couple of solutions for the shift-click problem come to mind:

  • prevent the user from shift-clicking recipes that have multiple, exclusive, weighted outputs, or
  • intercept the shift-click behavior, replace the item(s) being withdrawn from the crafting result slot, and deny the transfer if the player doesn't have enough room to do a full transfer of the resulting item stack

Option one seems clean and simple.

Option two could be gamed by the player. Because the weighted item would have to be newly picked for each shift-click attempt, the player could ensure that they got the item they wanted from the recipe by adjusting their inventory so they only had enough room for said item. To prevent that behavior, the craft result would have to be cached somewhere in order to remain consistent between shift-click attempts. At this point this solution begins to escalate in complexity. Where do we store it? Does it need to persist between world loads? What happens if the player loses/gains a gamestage, invalidating the recipe in between shift-click attempts?

Instead of denying the item transfer in option two, we could instead store the craft result in the player's cursor, pop the result into the world, or replace the contents of the output slot with the recipe's actual, calculated result. If we store the result in the player's cursor, they are forced to deal with it immediately, either dropping it on the ground or swapping it with an item in their full inventory. Popping the result into the world could confuse the player. The slot would suddenly go blank, their recipe ingredients would disappear and, since the gui is covering some of the screen, they might not see or hear the items pop out of the table. Having the result occupy the mouse cursor is closer to an expected behavior when clicking on a slot. Finally, storing the result in the table's result slot isn't quite as clean an option as the first two options. A flag would still need to be set and persisted that marked the slot as having an already calculated output to ensure that it doesn't get calculated again when the player next attempts to retrieve it.

I think the solution is to intercept the shift-click behavior, replace the item, and attempt to place the entire result into the player's inventory. If the entire result does not fit in the inventory, place the remaining items into the player's cursor.

So now the todo list looks like this (in order of priority):

  • Resolve an item dupe bug when shift-click crafting
  • Create documentation
  • MouseTweaks integration

I will resume work on this tomorrow if time permits; if not, the day after.

commented
  • Resolve an item dupe bug when shift-click crafting
  • Create documentation
  • MouseTweaks integration

Simply disabled MouseTweaks mouse wheel tweak for now.

commented

Feature available in 1.12.2-1.8.14

commented
  • Resolve an item dupe bug when shift-click crafting
  • Create documentation
  • MouseTweaks integration

The shift-click solution became too complex, duplicated too much vanilla code, and simply required a large amount of fiddly, error-prone code. I may revisit this again in the future, but for now, I've decided to simply disable shift-click behavior on recipes with multiple, weighted outputs. This is the "good enough for now" solution that allows me to get this update out faster.

commented
  • Resolve an item dupe bug when shift-click crafting
  • Create documentation
  • MouseTweaks integration
commented

Confirmed MouseTweaks behavior with multi-output recipes is pretty bugged out.