Skip to content
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

Crafting quality levels through item faults #53500

Open
I-am-Erk opened this issue Dec 16, 2021 · 10 comments
Open

Crafting quality levels through item faults #53500

I-am-Erk opened this issue Dec 16, 2021 · 10 comments
Labels
[C++] Changes (can be) made in C++. Previously named `Code` Crafting / Construction / Recipes Includes: Uncrafting / Disassembling Items / Item Actions / Item Qualities Items and how they work and interact (P3 - Medium) Medium (normal) priority <Suggestion / Discussion> Talk it out before implementing

Comments

@I-am-Erk
Copy link
Member

I-am-Erk commented Dec 16, 2021

Is your feature request related to a problem? Please describe.

Everyone wants item quality levels. This is a revamp of that linked issue, updated to the present day and made into far less of a pipe dream.

Presently we do not do a good job of simulating the difference between a well-made or a poorly made object. In addition, it would be nice if different salvage materials were able to be represented somehow.

Item quality levels seem to be the answer to both. However, there are a few issues as brought up in discussion with Kevin, mlangsdorf, Venera3, and probably around half of the core contributors/devs I have discussed this with in the last two years. Made brief:

  • Item quality should not allow one item (eg a hand-forged hacksaw) to supercede a higher level, usually pre-cataclysm item (eg. a high speed steel hacksaw).
  • Inheriting item materials (this hand-forged hacksaw is made from oak wood and aluminium and low quality steel) is not desirable as it would be nearly impossible to figure out a rational system with all the variables involved.

Further to this, ongoing discussion of how we might implement this has made me realize that "item quality" means a very different thing depending on what we're talking about. If you're making clothing, it might mean more storage space or less encumbrance. If you're making food, it might mean it keeps longer, or tastes better.

Solution you would like.

We already have a pretty robust and underutilized system for faults in items. I suggest we utilize this to create item qualities: in essence, we create a few general lists of item faults, and rules to apply them to specific items and categories of items. The collection of faults that an item has determines its quality.

What this means first, and this is a great help to rational design, is that the basic JSON definition of an item represents its ideal form. The JSON entry for hammer should represent the absolute best hammer you can ever get by any means, the top quality platonic ideal of a hammer. Even the JSON entry for something like a "makeshift hammer" would still be the best makeshift hammer you could ever expect to make, a complete masterpiece of duct-taping metal scrap to a stick.

Starting out: Cooking

Initially I think we should test this system out on cooking, and leave future implementations for other item categories until the cooking system is worked out very nicely. There are a few reasons for that:

  • cooking has a lot of great examples where it is obviously wrong for a flaw to be applied, such as getting "oversalted clean water" or something.
  • cooking is easy to intuitively understand and a lot of people know how it works.
  • cooking is easy to sort into a lot of different subcategories by types that should get similar flaws applied
  • food items are one of the most quality-susceptible crafts, and our current failure system is modeling this very poorly. Adding new fail states would dramatically improve the food crafting system.
  • food is something where it's usually going to be preferable to craft it yourself, not loot it, so making a more robust crafting system here is important.

JSONized expanded faults

The first thing we'll need is the ability to create a json entry for a fault. This should be pretty minimal. Over time, we'll need to add code support for various fault effects, but let's give some examples here.

{
  "id": "oversalted",
  "type": "fault",
  "disp_string": "oversalted",
  "effects": [ "fun_mult": 0.8, "fun_mod": -1 ],
  "severity": 2
},
{
  "id": "undercooked",
  "type": "fault",
  "disp_string": "undercooked",
  "effects": [ "fun_mult": 0.8, "fun_mod": -1, "spoil_mult": 0.8 ],
  "group": "cook_temperature",
  "severity": 2
},
{
  "id": "overcooked",
  "type": "fault",
  "disp_string": "overcooked",
  "effects": [ "fun_mult": 0.8, "fun_mod": -1 ],
  "group": "cook_temperature",
  "severity": 2
},
{
  "id": "burnt",
  "type": "fault",
  "disp_string": "burnt",
  "effects": [ "fun_mult": 0.5, "fun_mod": -4, "yield": 0.5 ],
  "group": "cook_temperature",
  "severity": 5
},
{
  "id": "charred",
  "type": "fault",
  "disp_string": "burnt to a crisp",
  "effects": [ "make_inedible": true ],
  "group": "cook_temperature",
  "severity": 10
}

Breaking it down:

  • "id" and "type": obvs
  • "disp_string": What do we call this on the player side
  • "effects": vector, accepts a list of effects defined elsewhere. What the fault does to the item.
  • "group": Optional field. Cancels other faults in this group, you can only have one fault from a given group at a time.
  • "severity": Used to determine how bad this fault is for our failure rolls, the more severe faults override less severe ones from their group and are harder to get through failure.

Applying faults to items

At first, I don't think we need to get into applying faults to items that spawn in the world. There will come a time for that, but for food, we can let the stuff that spawns come in "platonic ideal" forms. If there are cases where it doesn't seem like pre-cataclysm food should be better, we can always add an entry for eg. homemade jam that has better possible stats than the storebought stuff. So, for this first pass, I am only going to talk about faults added through crafting.

Fault list objects

First, it makes our lives easier if we can create standalone lists of related faults, and call on them as needed. These lists should include a weight for each fault on the list: the higher the weight, the more likely you are to get that fault. Initially we should keep these lists quite simple, eg;

{
  "id": "fl_cook_temperature",
  "type": "fault_list",
  "allow_faults": [
    { "fault": "undercooked", "weight": 10 },
    { "fault": "overcooked", "weight": 10 },
    { "fault": "burnt", "weight": 5 },
    { "fault": "charred", "weight": 1 }
  ]
}

In the long run, we will want the ability to load multiple faultlists from multiple sources, allowing an easier data-driven approach. The weights of different allowed faults should be added together.

Later, we should add a ban_faults attribute to the object, so that if we have multiple overlapping faults, we can remove some that don't make sense. Note that the sequence faults are added should not matter: a faultlist banning "burnt" should then prevent "burnt" from being added by a later faultlist. In code this probably means that we should first cycle through what faults have been allowed, then remove any that have been banned. This will be important when we get to faultlist inheritance, which adds a data driven approach that should make this a lot easier in the long run.

Fault lists in recipes

A given recipe entry now accepts a list attribute, "faults". Each entry requires a fault list ID and a weight stat, default 1. The weight of individual items in the given faultlist is multiplied by the weight stat if there is more than one faultlist present; in the example below, the recipe doesn't have very complex seasoning, so it is less likely you'll get a fault related to seasonings.

For example:

"faults": [
  { "list": "fl_cook_temperature", "weight": 1 },
  { "list": "fl_cook_seasoning", "weight": 0.5 }
]

Going forward long term, adding faults via recipes should be our last resort, though. It's appropriate for testing out the faults system, but in the long run we should be able to add fault lists to various upstream JSONs, and have them inherit to the recipe. I picture faultlists going into:

  • item category
  • skills used
  • tool qualities
  • specific ingredients
  • recipe book source

This list is in descending order of priority. Really, even if item category, skills used, and tool qualities provide baseline faultlists, then we can adjust details in the recipe itself and get a good amount of detail.

Replacing failure with faults

If a fault is defined in a recipe, we'll use a different algorithm for failure. If a crafting failure is rolled, ignore all the current follow up calculations. See handle_craft_failure. We should in fact just skip the current algorithm completely if there is a defined fault list. Instead, all the concepts currently included in failure should be recorded as a possible fault. That means that not every recipe will be possible to "mess up and lose a component" - we'd want to be able to define that as a fault.

I will write a better algorithm here, but in short to get this out before my lunch break ends: if a crafting roll fails by a small margin, we should just slow down the craft progress. If it fails by a larger margin, we should roll on the possible faults this craft can have, and add one. Lower rolls should favour high-severity faults. Rather than messing up and losing product, a full failure should result in an appropriate high-level fault that either makes the item useless or reduces its function to a sufficient degree.

We would need two additional "faults" that don't actually function as faults (ie. they are not attached to the final product item), but are entered as such into the JSON fault tables.

  • Mess up and lose a component: this would now be codable with specific messages and components to lose. For example, "you get distracted and burnt the toast to a crisp" might destroy your slice of bread in a "toast and butter" recipe, but would never destroy the butter.
  • Mess up and reduce product: For anything with a quantity >1 of product (charges or count), this is a chance to reduce the amount yielded. For example "You burnt your grilled cheese sandwich, but are able to salvage a lot of it" might give you 1 instead of 2 charges of sandwich in the end.

Not every recipe can be completely failed. You can make a raw salad that isn't good, but you can't easily chop up vegetables and mix them in such a way as to be both inedible and unrecoverable. This faults system should allow us to encapsulate that.

Some faults are removable?

Our existing faults system comes with a repair option, and that meshes well with this. In some cases you may be able to finish cooking your undercooked food item, for example. Most faults should not be trivially repairable (if it were, we should instead just increase crafting time) - a fix might mean a change of ingredients or tools, for example: a "bland" fault might be repaired with the addition of salt.

Alternatively, repairing the fault might introduce a new problem in some cases (like, cooking undercooked food a second time might add a new non-removable fault that makes it too dry, for a lessened penalty).

As a general rule of thumb, "quality" faults like these should not be removable though.

Order of implementation

This represents several PRs to complete. I am not great at code, so take this with a grain of salt, but I think the correct way to do this is:

  • Preparation: Brainstorm common faults appropriate for several item categories - food, clothing, weapons, and general. I have done some of this in this Gist note
  • Infrastructure PR: Add faultlist object and fault object, add demo versions of each. Nonfunctional at this time.
  • Prototype PR: Allow recipes to call faultlists and add faultlists to a couple test recipes.
  • Inheritance PR: Allow faultlists to inherit from item category, skill used, and tool quality. Add a few test uses for each.
  • Content addition calls: Crowdsource the addition of more faults.

Describe alternatives you have considered.

More complex quality levels as previously theorized but, for many reasons, never implemented.

Additional context

Later, we should allow certain factors that might make a specific fault more or less likely. That factor can be applied at the list level applying to all items in the list, at the recipe level when the list is called, or at the individual item level. If applied more than once, recipe calls override general list properties but individual list item properties override recipe level. Some example factors:

  • Tool quality - present/absent or by level, so eg. the higher the cut quality the less likely this fault is to occur
  • Proficiencies present/absent
  • using certain substitute ingredients
    Particular ingredient/tool items could also have in their own JSON an attribute that increases or decreases the likelihood of certain faults, if those faults are possible.
@I-am-Erk I-am-Erk added <Suggestion / Discussion> Talk it out before implementing [C++] Changes (can be) made in C++. Previously named `Code` Crafting / Construction / Recipes Includes: Uncrafting / Disassembling Items / Item Actions / Item Qualities Items and how they work and interact 0.G Project Freeze labels Dec 16, 2021
@Michael1993
Copy link
Contributor

Michael1993 commented Dec 24, 2021

The current fault system makes it possible to "mend" faulty items. How would that look like with a cooked item?

I guess you could have a 'food repair kit' (no clue what you'd call it) that takes salt (as ammo)?

I'm asking because you can repair faulty cooked items. You can cut off the burnt parts, add more salt, put it back into the oven if it's undercooked, etc.

@I-am-Erk
Copy link
Member Author

Not every fault needs to be mendable. You can't fix oversalted or burnt food, for example. We might have some fixable faults but the ones representing quality levels are often going to be permanent, especially for food.

@Michael1993
Copy link
Contributor

As an aside that I've been thinking about - how would this new system affect cutlass (replica) and cutlass (actual) or other such items? Completely unrelated? Integrated into the new system? Something that we don't even need to worry about because they are not in the scope of the current issue?

@I-am-Erk
Copy link
Member Author

I-am-Erk commented Jan 6, 2022

Eventually we may want those to be done with a faults system rather than as individual items, but it's quite a ways down the road and would go along with making it possible to add faults to non-food crafts, like having poor balance on the swing.

@coyo7e
Copy link

coyo7e commented Jan 28, 2022

I don't have much to add outside of being gobsmacked that the mend option works on anything outside of fouled guns.

Also I love that the OP correctly named-dropped "platonic ideal forms," which I haven't heard since my early philosophy classes and always try to use but nobody gets it. Allegory of the Cave, much? 👯

@stale
Copy link

stale bot commented Mar 2, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. Please do not 'bump' or comment on this issue unless you are actively working on it. Stale issues, and stale issues that are closed are still considered.

@stale stale bot added the stale Closed for lack of activity, but still valid. label Mar 2, 2022
@Michael1993
Copy link
Contributor

Michael1993 commented Sep 12, 2022

Hey, @I-am-Erk, I've been looking at this for a bit but I work with Java and it's been about a decade since I touched anything C++. Could you give me some pointers how I could start on this (if you have the time)?

Something like:

  • Example code I could study to read in JSON files into list(s)
  • Clarification on the handle craft failure: is your intention is to "add a fault to the final product then continue crafting (possibility to accumulate multiple faults from different fault groups)" or "mark item as faulty to roll a single fault when crafting is done"?
  • Also the codebase is huge and I'm having trouble with VS - where can I find the existing fault system? 😭

These would be useful to anyone trying to work on this issue (probably).

@stale stale bot removed the stale Closed for lack of activity, but still valid. label Sep 12, 2022
@hjk321
Copy link
Contributor

hjk321 commented Sep 19, 2022

@Michael1993 Hey there. The fault system including code to load it from JSON (function load_fault) is centered mostly in the fault.cpp file. Hope this helps, as I am in the same boat you are as far as cpp goes.

For your craft failure question, there's no pre-discussed answer but here's my two cents: Right now crafting is on a pass-fail system based on skill level. With a fault system we'd keep that skill bar in place to see if the player even succeeds at crafting the item, and then we set a second, higher skill bar that guarantees the item is crafted without faults. From there, we'd have a "curve" for each fault based on the skill levels in between the two values, to determine the probability of each fault. For now to keep it simple each fault's chance of appearing would be calculated independently of each other, but this could be improved in the future. Let me know what you think of this system.

@I-am-Erk
Copy link
Member Author

Updated post with some newer thoughts.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 6, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. Please do not bump or comment on this issue unless you are actively working on it. Stale issues, and stale issues that are closed are still considered.

@github-actions github-actions bot added the stale Closed for lack of activity, but still valid. label Dec 6, 2022
@I-am-Erk I-am-Erk added (P3 - Medium) Medium (normal) priority and removed stale Closed for lack of activity, but still valid. labels Dec 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[C++] Changes (can be) made in C++. Previously named `Code` Crafting / Construction / Recipes Includes: Uncrafting / Disassembling Items / Item Actions / Item Qualities Items and how they work and interact (P3 - Medium) Medium (normal) priority <Suggestion / Discussion> Talk it out before implementing
Projects
None yet
Development

No branches or pull requests

5 participants