Scripting:Items
Items are an area where lots of customization can be done, as they live mostly on Python side. However, this makes them a complex topic, where errors will often result in weird behaviour. Therefore, it is vital to get at least a basic understanding of the item implementation.
Inside the Engine
On the outside, all items are treated alike, but internally there is a huge difference between mutable and immutable items.
- Mutable items are all those items that can change their state throughout the game, like a torch that burns down, or a container that can be filled or emptied. Because of that, the engine has to keep track of each individual mutable item, which can become quite costly if there are a large number of them.
- Immutable items on the other hand – that is, items that don’t change, like a coin or an arrow – are not treated that way. Instead, exactly one copy of each immutable item type exists throughout the game. All instances of such an item are mere references to that single copy. This means, if an immutable item gets changed, all items of the same type will change as well!
That may sound very restrictive, but it isn’t. Immutable items can still be transformed into completely different items.
Basic Interface
Although items live mostly on Python side, that doesn’t mean that they are totally independent from the engine. Item management and persistence are part of the engine and it relies upon a specific interface. Fortunately, the item base class, defined in item.py, provides most of this interface already. As long as all items inherit (directly or indirectly) from the item class, little further work needs to be done. Only three things have to be taken care of:
- Constructor If an item requires an init method, it must not forget to call the constructor of its base class. Otherwise, some attributes of the item might remain unitialized, and errors will surely follow.
- Saving Each item must be able to save its current state. For that purpose, it has to provide the put_state method. Apart from saving its own attributes, it must not forget to save its base class.
- Loading Finally, an item must be able to restore its state. This is done via the get_state method. Again, items must also take care of loading the state of their base class.
The following script can serve as template for new items:
import base_class # # Item description goes here # class new_item (base_class.base_class): def __init__ (self): # -- init parent class base_class.base_class.__init__(self) # -- init attributes ... def [void] put_state (self, [base.flat] file): # -- save state of parent class base_class.base_class.put_state (self, file) # -- save attributes ... def [void] get_state (self, [base.flat] file): # -- load state of parent class base_class.base_class.get_state (self, file) # -- load attributes ...
Advanced Interface
While the methods described above are essential for item management, they do little in terms of defining an item’s properties and abilities. For that purpose, six more methods – the so called item actions – have been defined: pick up, drop, equip, unequip, combine and use. However, not every item needs to implement every action. Those that are not essential to an item’s functionality can be omitted. Purpose and constraints of each action are described in detail below.
- The pick up method is called when a character picks up an item from the ground.
- The drop method is called when a character is about to drop an item to the ground. It could be used to prevent characters from dropping (and losing) items important for the plot.
- The equip method serves two purposes. For one, it is used to test whether an item is equippable (by a certain character). If the equip method of an item returns 1, the item can be equipped. If it returns 0, or if the item has no equip method, the item cannot be equipped. Further, the equip method can also be used to apply any effects the item might have on the character.
- The unequip method is called when an equipped item is going to be removed. If it returns 1, or if the item has no unequip method, the item will be removed from the character. Items that cannot be removed (cursed items, for example) have to return 0 to indicate that they are not removable (at present)
- The combine method is used to implement actions involving exactly two items: an agent that is “applied” to a target. If the combination is possible, the target will be transformed, as the following examples demonstrate:
If the combination is sucessful, the resulting item has to be returned. If the two items cannot be combined, the method should return None instead. If the target item was stackable, the result must be stackable as well, and its maximum stack size must be at least as large as that of the target.target agent result lamp + lamp oil → lamp (refilled) stick + knife → wooden arrow arrow + poison → poisoned arrow - The use method contains the item's primary action, if it has one. Food or potions would be consumed when used, applying what special effects they have. The contents of books would be displayed, torches or candles lit, etc ...
Custom Interface
Apart from the basic and advanced interfaces that are required and used by the engine, items may of course provide further methods that are accessible from other scripts. Weapons for example might provide a deal_damage method to calculate the damage a character takes when being hit by that weapon.
Whether an item needs a custom interface depends mostly on other parts of a game, like rules or combat system. Some items won’t need additional methods, others will. It’s up to you to make the most out of this feature.