-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Correctly predict craftability of recipes with overlapping item requirements #36657
Correctly predict craftability of recipes with overlapping item requirements #36657
Conversation
While working on the next steps for this I discovered a bug in basecamp upgrades that needs to be fixed before this can be, so this will be on hold while I work on that. |
0427acf
to
7d724f2
Compare
The extra features I wanted are now done, although this is now built on top of #36740 If anyone has any reasonable suggestions for any names to give the (currently blank) uilist entries pictured there, I'd welcome them. I still want to do some more testing that partial crafting is still working correctly. |
7d724f2
to
cebf063
Compare
As I suspected, I had broken partial crafting. But it's fixed now. |
cebf063
to
e1cacf7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, I couldn't find anything actually wrong so I just made some nitpicking suggestions.
The rework of can_start_craft()
seems like a definite improvement to me, I too am not a huge fan of the 5%
constant as it is so arbitrary, but it keeps things relatively simple. I've toyed with the idea of trying to make it dynamic based on crafting time but haven't looked into it too deeply yet.
I think the basic UI you have implemented currently is totally fine for this PR. Obviously it can get ugly/hard to use in extreme cases but as far as I can tell such cases will most likely be quite rare given the current recipes. It's also FAR simpler to implement than my decision tree idea, though we could always give that a try if the UI becomes a serious issue.
I'm not very familiar with the basecamp code, but see nothing obvious amiss there after your fix in #36740. The changes to the core crafting code all make sense to me. I played around with it briefly locally and wasn't able to break it.
Congrats on clearing one of the trickier 0.E blockers!
Want to use this output in tests, and it's helpful for it to be more consistent.
This is a new class intended to permit refactoring requirement_data to avoid duplicated items which cause various issues, such as difficulty in determining whether an item is possible to craft. This just ads the class and some tests related to it. As yet, it is not being put to use.
To prevent consuming all time or memory in the event this hits a bad case (which is possible for maliciously crafted recipes).
Fixes CleverRaven#32311. Fixes CleverRaven#32554. Each recipe now has a deduped_requirements() in addition to its requirements, now accessed via simple_requirements(). Each caller has been ported to one or the other as appropriate. Refactor craftability checks so that the "only needs 5% of charge" property can now be specified via a flag, to obviate the need to reconstruct an entire requirements object just for that change. Limited in-game testing is promising so far.
When a recipe is not craftable, but all the ingredients are green in the crafting GUI, this might be confusing for players. Explain to them what's going on in this case by adding a new message.
Previously, when deduped_requirements_data contained multiple feasible selections of components, the game would always arbitrarily pick the first one, meaning that the player might consume items they had not intended to. Now, instead, offer the choice to the player in this situation. This is done via a uilist dialogue. To trim down the amount rendered in the dialogue, I added a new feature to requirements to format a requirements list while only showing those options which are available (the green ones) and use that here. As a side-effect, the player can now cancel the choice, so all the places calling this function now need to deal with the potential failure to make a choice.
e1cacf7
to
8370925
Compare
Summary
SUMMARY: Bugfixes "Correctly predict craftability of recipes with overlapping item requirements"
Purpose of change
Fixes #32311.
Fixes #32554.
When recipes have overlapping requirements the current implementation for checking for availability of those requirements does not properly take into account the fact that a single item cannot be used to satisfy more than one.
(Actually, for overlaps between components and tools, there is some code to check for this, but not between different component requirements.)
This causes problems with some recipes. In particular, survivor telescope is discussed in #32311. The most severe example of this phenomenon is
chainmail_suit_faraday
which has 6 requirements each of which can be satisfied either by some chainmail sheets or by a specific piece of chainmail armour.As things stand, the crafting GUI will display these items as being craftable, but if you try to craft them then various strange things can happen, including
debugmsg
errors, and you end up with an in-progress craft object with 0% progress which you can't continue nor disassemble to recover the ingredients. This is clearly unsatisfactory.Describe the solution
(Description of the solution adapted from the discussion in #32311).
The basic idea is to detect recipes with overlapping requirements and refactor them into a set of alternative requirements, each of which is internally non-overlapping, then try each of these in turn.
So, for example, the survivor telescope recipe currently has the requirements:
These would internally be changed to have two sets of requirements:
No change to the json is required, this is calculated as part of recipe finalization.
Implementation-wise, this is done via a new class
deduped_requirement_data
, which contains avector
ofrequirement_data
objects.Recipes now expose both representations of their requirements, because code can want either the original or the deduped version depending on its needs. The original version is typically smaller and easier to understand, so that's used for example for displaying the requirements to the player.
As another example, the vegetable soup recipe (as discussed in #32554) was a bit more complex (looks like it's since been edited to not have any overlap, but it's still a good example). It used to look essentially like:
and is now transformed into
With more overlapping requirements or more items in the overlap the number of alternatives rises fast, but nothing in the core game gets too absurd. The worst case is the aforementioned
chainmail_suit_faraday
, which requires 63 alternativerequirement_data
objects. I've set a limit of 100 as a sanity-check to prevent mods performing a denial-of-service attack on the algorithm.The crafting GUI now correctly displays when items are craftable.
As a side-effect, I have reworked how
player::can_start_craft
operates. This seeks to detect whether a craft can be started (but maybe not finished), which requires only 5% of the tool charges. It used to do this by creating a whole new requirements object with different tools requirements, but this interacted poorly with this new feature. So, instead, I've added a new flag that can be passed to the various test-craftability functions, which ultimately gets used at the very innermost function to tweak the number of tool charges sought. I'm a little unhappy at how the "5%" arbitrary constant is scattered all over the codebase; I think it might be good to refactor that further, but not in this PR.TODO:
Describe alternatives you've considered
Various options are discussed in the comments for #32311.
This PR only tackles overlapping item requirements. There could in principle be a similar issue with overlapping tools requirements, which could be solved in the same way. I haven't seen that be an issue in practice, though, so I'm hoping YAGNI. In particular, because tool charges are consumed gradually you will probably always at least be able to start the craft.
Testing
Added several new unit tests to test the requirement refactoring algorithm.
Experiments in-game to test the crafting GUI.
Additional context
The crafting code is a complex beast that deals with many semi-orthogonal but interconnected issues. This is a sufficiently invasive change that I'm fairly sure I must have broken something. In particular, I'm not confident I've used the correct
inventory
in all cases.Recipe finalization is already the slowest step in game data finalization, and this is making it even slower again. It's not too bad, even on a debug build as I'm using, but it's something we need to keep in mind.
(My apologies for adding a new function to
player
...)The new error message that appears in the crafting GUI for this situation:
Crafting a survivor telescope is possible with sufficient lenses:
When you craft and have all the lenses, you must now choose between these options:
Things can get more complicated for recipes like the faraday suit. Here are three options when you have a reasonable selection of chainmail items on hand:
Here's the most crazy possibility, when all 63 options are possible and you must choose from them all. The uilist code still handles it correctly. It doesn't look great, but it does work.