Some updates to the crafting contrib readme
This commit is contained in:
parent
055bbcfee3
commit
8e19017dc3
4 changed files with 159 additions and 78 deletions
|
|
@ -1,39 +1,39 @@
|
||||||
# Crafting system contrib
|
# Crafting system contrib
|
||||||
|
|
||||||
_Contrib by Griatch 2020_
|
_Contrib by Griatch 2020_
|
||||||
```versionadded:: 1.0
|
```versionadded:: 1.0
|
||||||
```
|
```
|
||||||
|
|
||||||
This contrib implements a full Crafting system that can be expanded and modified to fit your game.
|
This contrib implements a full Crafting system that can be expanded and modified to fit your game.
|
||||||
|
|
||||||
- See the [evennia/contrib/crafting/crafting.py API](api:evennia.contrib.crafting.crafting) for installation
|
- See the [evennia/contrib/crafting/crafting.py API](api:evennia.contrib.crafting.crafting) for installation
|
||||||
instructrions.
|
instructrions.
|
||||||
- See the [sword example](api:evennia.contrib.crafting.example_recipes) for an example of how to design
|
- See the [sword example](api:evennia.contrib.crafting.example_recipes) for an example of how to design
|
||||||
a crafting tree for crafting a sword from base elements.
|
a crafting tree for crafting a sword from base elements.
|
||||||
|
|
||||||
From in-game it uses the new `craft` command:
|
From in-game it uses the new `craft` command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> craft bread from flour, eggs, salt, water, yeast using oven, roller
|
> craft bread from flour, eggs, salt, water, yeast using oven, roller
|
||||||
> craft bandage from cloth using scissors
|
> craft bandage from cloth using scissors
|
||||||
```
|
```
|
||||||
|
|
||||||
The syntax is `craft <recipe> [from <ingredient>,...][ using <tool>,...]`.
|
The syntax is `craft <recipe> [from <ingredient>,...][ using <tool>,...]`.
|
||||||
|
|
||||||
The above example uses the `bread` *recipe* and requires `flour`, `eggs`, `salt`, `water` and `yeast` objects
|
The above example uses the `bread` *recipe* and requires `flour`, `eggs`, `salt`, `water` and `yeast` objects
|
||||||
to be in your inventory. These will be consumed as part of crafting (baking) the bread.
|
to be in your inventory. These will be consumed as part of crafting (baking) the bread.
|
||||||
|
|
||||||
The `oven` and `roller` are "tools" that can be either in your inventory or in your current location (you are not carrying an oven
|
The `oven` and `roller` are "tools" that can be either in your inventory or in your current location (you are not carrying an oven
|
||||||
around with you after all). Tools are *not* consumed in the crafting. If the added ingredients/tools matches
|
around with you after all). Tools are *not* consumed in the crafting. If the added ingredients/tools matches
|
||||||
the requirements of the recipe, a new `bread` object will appear in the crafter's inventory.
|
the requirements of the recipe, a new `bread` object will appear in the crafter's inventory.
|
||||||
|
|
||||||
If you wanted, you could also picture recipes without any consumables:
|
If you wanted, you could also picture recipes without any consumables:
|
||||||
|
|
||||||
```
|
```
|
||||||
> craft fireball using wand, spellbook
|
> craft fireball using wand, spellbook
|
||||||
```
|
```
|
||||||
|
|
||||||
With a little creativity, the 'recipe' concept could be adopted to all sorts of things, like puzzles or
|
With a little creativity, the 'recipe' concept could be adopted to all sorts of things, like puzzles or
|
||||||
magic systems.
|
magic systems.
|
||||||
|
|
||||||
In code, you can craft using the `evennia.contrib.crafting.crafting.craft` function:
|
In code, you can craft using the `evennia.contrib.crafting.crafting.craft` function:
|
||||||
|
|
@ -46,22 +46,22 @@ result = craft(caller, "recipename", *inputs)
|
||||||
```
|
```
|
||||||
Here, `caller` is the one doing the crafting and `*inputs` is any combination of consumables and/or tool
|
Here, `caller` is the one doing the crafting and `*inputs` is any combination of consumables and/or tool
|
||||||
Objects. The system will identify which is which by the [Tags](../Components/Tags) on them (see below)
|
Objects. The system will identify which is which by the [Tags](../Components/Tags) on them (see below)
|
||||||
The `result` is always a list.
|
The `result` is always a list.
|
||||||
|
|
||||||
## Adding new recipes
|
## Adding new recipes
|
||||||
|
|
||||||
A *recipe* is a class inheriting from `evennia.contrib.crafting.crafting.CraftingRecipe`. This class
|
A *recipe* is a class inheriting from `evennia.contrib.crafting.crafting.CraftingRecipe`. This class
|
||||||
implements the most common form of crafting - that using in-game objects. Each recipe is a separate class
|
implements the most common form of crafting - that using in-game objects. Each recipe is a separate class
|
||||||
which gets initialized with the consumables/tools you provide.
|
which gets initialized with the consumables/tools you provide.
|
||||||
|
|
||||||
For the `craft` command to find your custom recipes, you need to tell Evennia where they are. Add a new
|
For the `craft` command to find your custom recipes, you need to tell Evennia where they are. Add a new
|
||||||
line to your `mygame/server/conf/settings.py` file, with a list to any new modules with recipe classes.
|
line to your `mygame/server/conf/settings.py` file, with a list to any new modules with recipe classes.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
CRAFT_RECIPE_MODULES = ["world.myrecipes"]
|
CRAFT_RECIPE_MODULES = ["world.myrecipes"]
|
||||||
```
|
```
|
||||||
|
|
||||||
(You need to reload after adding this). All global-level classes in these modules (whose names don't start
|
(You need to reload after adding this). All global-level classes in these modules (whose names don't start
|
||||||
with underscore) are considered by the system as viable recipes.
|
with underscore) are considered by the system as viable recipes.
|
||||||
|
|
||||||
Here we assume you created `mygame/world/myrecipes.py` to match the above example setting:
|
Here we assume you created `mygame/world/myrecipes.py` to match the above example setting:
|
||||||
|
|
@ -80,23 +80,23 @@ class WoodenPuppetRecipe(CraftingRecipe):
|
||||||
{"key": "A carved wooden doll",
|
{"key": "A carved wooden doll",
|
||||||
"typeclass": "typeclasses.objects.decorations.Toys",
|
"typeclass": "typeclasses.objects.decorations.Toys",
|
||||||
"desc": "A small carved doll"}
|
"desc": "A small carved doll"}
|
||||||
]
|
]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This specifies which tags to look for in the inputs. It defines a [Prototype](../Components/Prototypes)
|
This specifies which tags to look for in the inputs. It defines a [Prototype](../Components/Prototypes)
|
||||||
for the recipe to use to spawn the result on the fly (a recipe could spawn more than one result if needed).
|
for the recipe to use to spawn the result on the fly (a recipe could spawn more than one result if needed).
|
||||||
Instead of specifying the full prototype-dict, you could also just provide a list of `prototype_key`s to
|
Instead of specifying the full prototype-dict, you could also just provide a list of `prototype_key`s to
|
||||||
existing prototypes you have.
|
existing prototypes you have.
|
||||||
|
|
||||||
After reloading the server, this recipe would now be available to use. To try it we should
|
After reloading the server, this recipe would now be available to use. To try it we should
|
||||||
create materials and tools to insert into the recipe.
|
create materials and tools to insert into the recipe.
|
||||||
|
|
||||||
|
|
||||||
The recipe analyzes inputs, looking for [Tags](../Components/Tags) with specific tag-categories.
|
The recipe analyzes inputs, looking for [Tags](../Components/Tags) with specific tag-categories.
|
||||||
The tag-category used can be set per-recipe using the (`.consumable_tag_category` and
|
The tag-category used can be set per-recipe using the (`.consumable_tag_category` and
|
||||||
`.tool_tag_category` respectively). The defaults are `crafting_material` and `crafting_tool`. For
|
`.tool_tag_category` respectively). The defaults are `crafting_material` and `crafting_tool`. For
|
||||||
the puppet we need one object with the `wood` tag and another with the `knife` tag:
|
the puppet we need one object with the `wood` tag and another with the `knife` tag:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia import create_object
|
from evennia import create_object
|
||||||
|
|
@ -105,9 +105,9 @@ knife = create_object(key="Hobby knife", tags=[("knife", "crafting_tool")])
|
||||||
wood = create_object(key="Piece of wood", tags[("wood", "crafting_material")])
|
wood = create_object(key="Piece of wood", tags[("wood", "crafting_material")])
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the objects can have any name, all that matters is the tag/tag-category. This means if a
|
Note that the objects can have any name, all that matters is the tag/tag-category. This means if a
|
||||||
"bayonet" also had the "knife" crafting tag, it could also be used to carve a puppet. This is also
|
"bayonet" also had the "knife" crafting tag, it could also be used to carve a puppet. This is also
|
||||||
potentially interesting for use in puzzles and to allow users to experiment and find alternatives to
|
potentially interesting for use in puzzles and to allow users to experiment and find alternatives to
|
||||||
know ingredients.
|
know ingredients.
|
||||||
|
|
||||||
By the way, there is also a simple shortcut for doing this:
|
By the way, there is also a simple shortcut for doing this:
|
||||||
|
|
@ -116,15 +116,15 @@ By the way, there is also a simple shortcut for doing this:
|
||||||
tools, consumables = WoodenPuppetRecipe.seed()
|
tools, consumables = WoodenPuppetRecipe.seed()
|
||||||
```
|
```
|
||||||
|
|
||||||
The `seed` class-method will create simple dummy objects that fulfills the recipe's requirements. This
|
The `seed` class-method will create simple dummy objects that fulfills the recipe's requirements. This
|
||||||
is great for testing.
|
is great for testing.
|
||||||
|
|
||||||
Assuming these objects were put in our inventory, we could now craft using the in-game command:
|
Assuming these objects were put in our inventory, we could now craft using the in-game command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> craft wooden puppet from wood using hobby knife
|
> craft wooden puppet from wood using hobby knife
|
||||||
```
|
```
|
||||||
In code we would do
|
In code we would do
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from evennia.contrub.crafting.crafting import craft
|
from evennia.contrub.crafting.crafting import craft
|
||||||
|
|
@ -132,7 +132,7 @@ puppet = craft(crafter, "wooden puppet", knife, wood)
|
||||||
|
|
||||||
```
|
```
|
||||||
In the call to `craft`, the order of `knife` and `wood` doesn't matter - the recipe will sort out which
|
In the call to `craft`, the order of `knife` and `wood` doesn't matter - the recipe will sort out which
|
||||||
is which based on their tags.
|
is which based on their tags.
|
||||||
|
|
||||||
## Deeper customization of recipes
|
## Deeper customization of recipes
|
||||||
|
|
||||||
|
|
@ -147,36 +147,36 @@ recipe = MyRecipe(crafter, *(tools + consumables))
|
||||||
result = recipe.craft()
|
result = recipe.craft()
|
||||||
|
|
||||||
```
|
```
|
||||||
This is useful for testing and allows you to use the class directly without adding it to a module
|
This is useful for testing and allows you to use the class directly without adding it to a module
|
||||||
in `settings.CRAFTING_RECIPE_MODULES`.
|
in `settings.CRAFTING_RECIPE_MODULES`.
|
||||||
|
|
||||||
Even without modifying more than the class properties, there are a lot of options to set on
|
Even without modifying more than the class properties, there are a lot of options to set on
|
||||||
the `CraftingRecipe` class. Easiest is to refer to the
|
the `CraftingRecipe` class. Easiest is to refer to the
|
||||||
[CraftingRecipe api documentation](evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe).
|
[CraftingRecipe api documentation](evennia.contrib.crafting.crafting.html#evennia.contrib.crafting.crafting.CraftingRecipe).
|
||||||
For example, you can customize the validation-error messages, decide if the ingredients have
|
For example, you can customize the validation-error messages, decide if the ingredients have
|
||||||
to be exactly right, if a failure still consumes the ingredients or not, and much more.
|
to be exactly right, if a failure still consumes the ingredients or not, and much more.
|
||||||
|
|
||||||
For even more control you can override hooks in your own class:
|
For even more control you can override hooks in your own class:
|
||||||
|
|
||||||
- `pre_craft` - this should handle input validation and store its data in `.validated_consumables` and
|
- `pre_craft` - this should handle input validation and store its data in `.validated_consumables` and
|
||||||
`validated_tools` respectively. On error, this reports the error to the crafter and raises the
|
`validated_tools` respectively. On error, this reports the error to the crafter and raises the
|
||||||
`CraftingValidationError`.
|
`CraftingValidationError`.
|
||||||
- `do_craft` - this will only be called if `pre_craft` finished without an exception. This should
|
- `craft` - this will only be called if `pre_craft` finished without an exception. This should
|
||||||
return the result of the crafting, by spawnging the prototypes. Or the empty list if crafting
|
return the result of the crafting, by spawnging the prototypes. Or the empty list if crafting
|
||||||
fails for some reason. This is the place to add skill-checks or random chance if you need it
|
fails for some reason. This is the place to add skill-checks or random chance if you need it
|
||||||
for your game.
|
for your game.
|
||||||
- `post_craft` - this receives the result from `do_craft` and handles error messages and also deletes
|
- `post_craft` - this receives the result from `craft` and handles error messages and also deletes
|
||||||
any consumables as needed. It may also modify the result before returning it.
|
any consumables as needed. It may also modify the result before returning it.
|
||||||
- `msg` - this is a wrapper for `self.crafter.msg` and should be used to send messages to the
|
- `msg` - this is a wrapper for `self.crafter.msg` and should be used to send messages to the
|
||||||
crafter. Centralizing this means you can also easily modify the sending style in one place later.
|
crafter. Centralizing this means you can also easily modify the sending style in one place later.
|
||||||
|
|
||||||
The class constructor (and the `craft` access function) takes optional `**kwargs`. These are passed
|
The class constructor (and the `craft` access function) takes optional `**kwargs`. These are passed
|
||||||
into each crafting hook. These are unused by default but could be used to customize things per-call.
|
into each crafting hook. These are unused by default but could be used to customize things per-call.
|
||||||
|
|
||||||
### Skilled crafters
|
### Skilled crafters
|
||||||
|
|
||||||
What the crafting system does not have out of the box is a 'skill' system - the notion of being able
|
What the crafting system does not have out of the box is a 'skill' system - the notion of being able
|
||||||
to fail the craft if you are not skilled enough. Just how skills work is game-dependent, so to add
|
to fail the craft if you are not skilled enough. Just how skills work is game-dependent, so to add
|
||||||
this you need to make your own recipe parent class and have your recipes inherit from this.
|
this you need to make your own recipe parent class and have your recipes inherit from this.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -189,7 +189,7 @@ class SkillRecipe(CraftingRecipe):
|
||||||
|
|
||||||
difficulty = 20
|
difficulty = 20
|
||||||
|
|
||||||
def do_craft(self, **kwargs):
|
def craft(self, **kwargs):
|
||||||
"""The input is ok. Determine if crafting succeeds"""
|
"""The input is ok. Determine if crafting succeeds"""
|
||||||
|
|
||||||
# this is set at initialization
|
# this is set at initialization
|
||||||
|
|
@ -201,15 +201,15 @@ class SkillRecipe(CraftingRecipe):
|
||||||
# roll for success:
|
# roll for success:
|
||||||
if randint(1, 100) <= (crafting_skill - self.difficulty):
|
if randint(1, 100) <= (crafting_skill - self.difficulty):
|
||||||
# all is good, craft away
|
# all is good, craft away
|
||||||
return super().do_craft()
|
return super().craft()
|
||||||
else:
|
else:
|
||||||
self.msg("You are not good enough to craft this. Better luck next time!")
|
self.msg("You are not good enough to craft this. Better luck next time!")
|
||||||
return []
|
return []
|
||||||
```
|
```
|
||||||
In this example we introduce a `.difficulty` for the recipe and makes a 'dice roll' to see
|
In this example we introduce a `.difficulty` for the recipe and makes a 'dice roll' to see
|
||||||
if we succed. We would of course make this a lot more immersive and detailed in a full game. In
|
if we succed. We would of course make this a lot more immersive and detailed in a full game. In
|
||||||
principle you could customize each recipe just the way you want it, but you could also inherit from
|
principle you could customize each recipe just the way you want it, but you could also inherit from
|
||||||
a central parent like this to cut down on work.
|
a central parent like this to cut down on work.
|
||||||
|
|
||||||
The [sword recipe example module](api:evennia.contrib.crafting.example_recipes) also shows an example
|
The [sword recipe example module](api:evennia.contrib.crafting.example_recipes) also shows an example
|
||||||
of a random skill-check being implemented in a parent and then inherited for multiple use.
|
of a random skill-check being implemented in a parent and then inherited for multiple use.
|
||||||
|
|
@ -218,5 +218,5 @@ of a random skill-check being implemented in a parent and then inherited for mul
|
||||||
|
|
||||||
If you want to build something even more custom (maybe using different input types of validation logic)
|
If you want to build something even more custom (maybe using different input types of validation logic)
|
||||||
you could also look at the `CraftingRecipe` parent class `CraftingRecipeBase`.
|
you could also look at the `CraftingRecipe` parent class `CraftingRecipeBase`.
|
||||||
It implements just the minimum needed to be a recipe and for big changes you may be better off starting
|
It implements just the minimum needed to be a recipe and for big changes you may be better off starting
|
||||||
from this rather than the more opinionated `CraftingRecipe`.
|
from this rather than the more opinionated `CraftingRecipe`.
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,102 @@
|
||||||
# Crafting system
|
# Crafting system
|
||||||
|
|
||||||
Contrib - Griatch 2020
|
Contrib - Griatch 2020
|
||||||
|
|
||||||
This implements a full crafting system. The principle is that of a 'recipe':
|
This implements a full crafting system. The principle is that of a 'recipe':
|
||||||
|
|
||||||
object1 + object2 + ... -> craft_recipe -> objectA, objectB, ...
|
ingredient1 + ingredient2 + ... + tool1 + tool2 + ... + craft_recipe -> objectA, objectB, ...
|
||||||
|
|
||||||
The recipe is a class that specifies input and output hooks. By default the
|
Here, 'ingredients' are consumed by the crafting process, whereas 'tools' are
|
||||||
input is a list of object-tags (using the "crafting_material" tag-category)
|
necessary for the process by will not be destroyed by it.
|
||||||
and objects passing this check must be passed into the recipe.
|
|
||||||
|
|
||||||
The output is given by a set of prototypes. If the input is correct and other
|
An example would be to use the tools 'bowl' and 'oven' to use the ingredients
|
||||||
checks are passed (such as crafting skill, for example), these prototypes will
|
'flour', 'salt', 'yeast' and 'water' to create 'bread' using the 'bread recipe'.
|
||||||
be used to generate the new objects being 'crafted'.
|
|
||||||
|
|
||||||
Each recipe is a stand-alone entity which allows for very advanced customization
|
A recipe does not have to use tools, like 'snow' + 'snowball-recipe' becomes
|
||||||
for every recipe - for example one could have a recipe where the input ingredients
|
'snowball'. Conversely one could also imagine using tools without consumables,
|
||||||
are not destroyed in the process, or which require other properties of the input
|
like using 'spell book' and 'wand' to produce 'fireball' by having the recipe
|
||||||
(such as a 'quality').
|
check some magic skill on the character.
|
||||||
|
|
||||||
|
The system is generic enough to be used also for adventure-like puzzles, like
|
||||||
|
combining 'stick', 'string' and 'hook' to get a 'makeshift fishing rod' that
|
||||||
|
you can use with 'storm drain' (treated as a tool) to get 'key' ...
|
||||||
|
|
||||||
|
## Intallation and Usage
|
||||||
|
|
||||||
|
Import the `CmdCraft` command from evennia/contrib/crafting/crafting.py and
|
||||||
|
add it to your Character cmdset. Reload and the `craft` command will be
|
||||||
|
available to you:
|
||||||
|
|
||||||
|
craft <recipe> [from <ingredient>,...] [using <tool>, ...]
|
||||||
|
|
||||||
|
For example
|
||||||
|
|
||||||
|
craft toy car from plank, wooden wheels, nails using saw, hammer
|
||||||
|
|
||||||
|
To use crafting you need recipes. Add a new variable to `mygame/server/conf/settings.py`:
|
||||||
|
|
||||||
|
CRAFT_RECIPE_MODULES = ['world.recipes']
|
||||||
|
|
||||||
|
All top-level classes in these modules (whose name does not start with `_`)
|
||||||
|
will be parsed by Evennia as recipes to make available to the crafting system.
|
||||||
|
Using the above example, create `mygame/world/recipes.py` and add your recipies
|
||||||
|
in there:
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
from evennia.contrib.crafting.crafting import CraftingRecipe, CraftingValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class RecipeBread(CraftingRecipe):
|
||||||
|
"""
|
||||||
|
Bread is good for making sandwitches!
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "bread" # used to identify this recipe in 'craft' command
|
||||||
|
tool_tags = ["bowl", "oven"]
|
||||||
|
consumable_tags = ["flour", "salt", "yeast", "water"]
|
||||||
|
output_prototypes = [
|
||||||
|
{"key": "Loaf of Bread",
|
||||||
|
"aliases": ["bread"],
|
||||||
|
"desc": "A nice load of bread.",
|
||||||
|
"typeclass": "typeclasses.objects.Food", # assuming this exists
|
||||||
|
"tags": [("bread", "crafting_material")] # this makes it usable in other recipes ...
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
def pre_craft(self, **kwargs):
|
||||||
|
# validates inputs etc. Raise `CraftingValidationError` if fails
|
||||||
|
|
||||||
|
def craft(self, **kwargs):
|
||||||
|
# performs the craft - but it can still fail (check skills etc here)
|
||||||
|
|
||||||
|
def craft(self, result, **kwargs):
|
||||||
|
# any post-crafting effects. Always called, even if crafting failed (be
|
||||||
|
# result would be None then)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical
|
||||||
|
|
||||||
|
The Recipe is a class that specifies the consumables, tools and output along
|
||||||
|
with various methods (that you can override) to do the the validation of inputs
|
||||||
|
and perform the crafting itself.
|
||||||
|
|
||||||
|
By default the input is a list of object-tags (using the "crafting_material"
|
||||||
|
and "crafting_tool" tag-categories respectively). Providing a set of objects
|
||||||
|
matching these tags are required for the crafting to be done. The use of tags
|
||||||
|
means that multiple different objects could all work for the same recipe, as
|
||||||
|
long as they have the right tag. This can be very useful for allowing players
|
||||||
|
to experiment and explore alternative ways to create things!
|
||||||
|
|
||||||
|
The output is given by a set of prototype-dicts. If the input is correct and
|
||||||
|
other checks are passed (such as crafting skill, for example), these prototypes
|
||||||
|
will be used to generate the new object(s) being crafted.
|
||||||
|
|
||||||
|
Each recipe is a stand-alone entity which allows for very advanced
|
||||||
|
customization for every recipe - for example one could have a recipe that
|
||||||
|
checks other properties of the inputs (like quality, color etc) and have that
|
||||||
|
affect the result. Your recipes could also (and likely would) tie into your
|
||||||
|
game's skill system to determine the success or outcome of the crafting.
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ class CraftingRecipeBase:
|
||||||
are optional but will be passed into all of the following hooks.
|
are optional but will be passed into all of the following hooks.
|
||||||
2. `.pre_craft(**kwargs)` - this normally validates inputs and stores them in
|
2. `.pre_craft(**kwargs)` - this normally validates inputs and stores them in
|
||||||
`.validated_inputs.`. Raises `CraftingValidationError` otherwise.
|
`.validated_inputs.`. Raises `CraftingValidationError` otherwise.
|
||||||
4. `.do_craft(**kwargs)` - should return the crafted item(s) or the empty list. Any
|
4. `.craft(**kwargs)` - should return the crafted item(s) or the empty list. Any
|
||||||
crafting errors should be immediately reported to user.
|
crafting errors should be immediately reported to user.
|
||||||
5. `.post_craft(crafted_result, **kwargs)`- always called, even if `pre_craft`
|
5. `.post_craft(crafted_result, **kwargs)`- always called, even if `pre_craft`
|
||||||
raised a `CraftingError` or `CraftingValidationError`.
|
raised a `CraftingError` or `CraftingValidationError`.
|
||||||
|
|
@ -252,7 +252,7 @@ class CraftingRecipeBase:
|
||||||
else:
|
else:
|
||||||
raise CraftingValidationError
|
raise CraftingValidationError
|
||||||
|
|
||||||
def do_craft(self, **kwargs):
|
def craft(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Hook to override.
|
Hook to override.
|
||||||
|
|
||||||
|
|
@ -277,7 +277,7 @@ class CraftingRecipeBase:
|
||||||
method is to delete the inputs.
|
method is to delete the inputs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
crafting_result (any): The outcome of crafting, as returned by `do_craft`.
|
crafting_result (any): The outcome of crafting, as returned by `craft()`.
|
||||||
**kwargs: Any extra flags passed at initialization.
|
**kwargs: Any extra flags passed at initialization.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -324,7 +324,7 @@ class CraftingRecipeBase:
|
||||||
if raise_exception:
|
if raise_exception:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
craft_result = self.do_craft(**craft_kwargs)
|
craft_result = self.craft(**craft_kwargs)
|
||||||
finally:
|
finally:
|
||||||
craft_result = self.post_craft(craft_result, **craft_kwargs)
|
craft_result = self.post_craft(craft_result, **craft_kwargs)
|
||||||
except (CraftingError, CraftingValidationError):
|
except (CraftingError, CraftingValidationError):
|
||||||
|
|
@ -455,7 +455,7 @@ class CraftingRecipe(CraftingRecipeBase):
|
||||||
3. `.pre_craft(**kwargs)` should handle validation of inputs. Results should
|
3. `.pre_craft(**kwargs)` should handle validation of inputs. Results should
|
||||||
be stored in `validated_consumables/tools` respectively. Raises `CraftingValidationError`
|
be stored in `validated_consumables/tools` respectively. Raises `CraftingValidationError`
|
||||||
otherwise.
|
otherwise.
|
||||||
4. `.do_craft(**kwargs)` will not be called if validation failed. Should return
|
4. `.craft(**kwargs)` will not be called if validation failed. Should return
|
||||||
a list of the things crafted.
|
a list of the things crafted.
|
||||||
5. `.post_craft(crafting_result, **kwargs)` is always called, also if validation
|
5. `.post_craft(crafting_result, **kwargs)` is always called, also if validation
|
||||||
failed (`crafting_result` will then be falsy). It does any cleanup. By default
|
failed (`crafting_result` will then be falsy). It does any cleanup. By default
|
||||||
|
|
@ -819,7 +819,7 @@ class CraftingRecipe(CraftingRecipeBase):
|
||||||
self.validated_tools = tools
|
self.validated_tools = tools
|
||||||
self.validated_consumables = consumables
|
self.validated_consumables = consumables
|
||||||
|
|
||||||
def do_craft(self, **kwargs):
|
def craft(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Hook to override. This will not be called if validation in `pre_craft`
|
Hook to override. This will not be called if validation in `pre_craft`
|
||||||
fails.
|
fails.
|
||||||
|
|
@ -847,7 +847,7 @@ class CraftingRecipe(CraftingRecipeBase):
|
||||||
this method is to delete the inputs.
|
this method is to delete the inputs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
craft_result (list): The crafted result, provided by `self.do_craft`.
|
craft_result (list): The crafted result, provided by `self.craft()`.
|
||||||
**kwargs (any): Passed from `self.craft`.
|
**kwargs (any): Passed from `self.craft`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -958,7 +958,6 @@ class CmdCraft(Command):
|
||||||
things in the current location, like a furnace, windmill or anvil.
|
things in the current location, like a furnace, windmill or anvil.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = "craft"
|
key = "craft"
|
||||||
locks = "cmd:all()"
|
locks = "cmd:all()"
|
||||||
help_category = "General"
|
help_category = "General"
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ class TestCraftingRecipeBase(TestCase):
|
||||||
"""Test craft hook, the main access method."""
|
"""Test craft hook, the main access method."""
|
||||||
|
|
||||||
expected_result = _TestMaterial("test_result")
|
expected_result = _TestMaterial("test_result")
|
||||||
self.recipe.do_craft = mock.MagicMock(return_value=expected_result)
|
self.recipe.craft = mock.MagicMock(return_value=expected_result)
|
||||||
|
|
||||||
self.assertTrue(self.recipe.allow_craft)
|
self.assertTrue(self.recipe.allow_craft)
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ class TestCraftingRecipeBase(TestCase):
|
||||||
|
|
||||||
# check result
|
# check result
|
||||||
self.assertEqual(result, expected_result)
|
self.assertEqual(result, expected_result)
|
||||||
self.recipe.do_craft.assert_called_with(kw1=1, kw2=2)
|
self.recipe.craft.assert_called_with(kw1=1, kw2=2)
|
||||||
|
|
||||||
# since allow_reuse is False, this usage should now be turned off
|
# since allow_reuse is False, this usage should now be turned off
|
||||||
self.assertFalse(self.recipe.allow_craft)
|
self.assertFalse(self.recipe.allow_craft)
|
||||||
|
|
@ -110,7 +110,7 @@ class TestCraftingRecipeBase(TestCase):
|
||||||
def test_craft_hook__fail(self):
|
def test_craft_hook__fail(self):
|
||||||
"""Test failing the call"""
|
"""Test failing the call"""
|
||||||
|
|
||||||
self.recipe.do_craft = mock.MagicMock(return_value=None)
|
self.recipe.craft = mock.MagicMock(return_value=None)
|
||||||
|
|
||||||
# trigger exception
|
# trigger exception
|
||||||
with self.assertRaises(crafting.CraftingError):
|
with self.assertRaises(crafting.CraftingError):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue