Drama¶
The previous sections of this manual have introduced you to the classes which Balladeer defines so that you can build your narrative.
The place where you write your code is called the Drama.
- class Drama(self, *args, world=None, config=None, **kwargs)¶
One important aspect of the Drama class is that inherits from
Entity
.
This means you can assign
State
to it.
It’s a good idea to customize your drama with Python properties so that you have access to useful characteristics. One of the supplied examples shows how the Drama class keeps track of the current location of the narrative.
@property
def here(self):
return self.get_state("Spot")
In addition to properties, there are three ways of adding functionality.
Overriding the Interlude method.
As a Directive handler.
In a Dialogue generator method.
Interlude¶
In Balladeer the interlude gets called every turn of a Story. This makes it the ideal place to put game rules which are invariant.
Directive handler¶
SpeechMark has a feature called directives. When a drama object implements a directive handler, it may be invoked from Speech.
Handlers must be instance methods whose name begins with the prefix on_
.
They take an argument which is the primary entity of the directive.
Any other entities are supplied as positional arguments.
- drama.on_xxxing(self, entity: Entity, *args: tuple[Entity], **kwargs):
Handles the SpeechMark directive ‘xxxing’.
- Parameters:
entity – The primary entity of the directive
args – The associated entities of the directive
- Return type:
None
This is a snippet from the Balladeer examples. In the scene, one character attacks another:
<WEAPON.attacking@FIGHTER_2>
_Whack!_
Here is the Drama class with the corresponding handler:
class Fight(Drama):
def on_attacking(self, entity: Entity, *args: tuple[Entity], **kwargs):
for enemy in args:
enemy.set_state(0)
Dialogue generator¶
Dialogue generators must be instance methods whose name begins with the prefix do_
.
They take keyword arguments, each of which must have an annotation.
The method must contain a docstring which defines the text that triggers the method. Docstrings may contain format specifiers which reference the keyword arguments.
- drama.do_xxx(self, this, text, director, *args, **kwargs):
Annotations for keyword arguments may be:
An iterable type eg: an Enum/State.
A string which gives attribute access via the drama object to an iterable of entities.
This method is a generator of Speech objects. The speech objects must be of these three classes:
Prologue
Speech which belongs at the top of a page.
Dialogue
Speech interleaved with the main content of the scene.
Epilogue
Speech which belongs at the bottom of a page.
This sounds complicated, but it’s easily demonstrated with a couple of examples.
This do_move
method declares in its docstring that text like n
, north
, go n
or go north
will activate the method. The method will be invoked with the corresponding
Compass
class member supplied via the heading parameter.
def do_move(self, this, text, director, *args, heading: Compass, **kwargs):
"""
{heading.name} | {heading.label}
go {heading.name} | go {heading.label}
"""
options = {
compass: spot for compass, spot, transit in self.world.map.options(self.here)
}
if heading not in options:
yield Prologue(f"<> You can't go {heading.label} from here.")
else:
self.set_state(options[heading])
Next we have do_drop
, an example of an annotation which is a string accessor on the drama object.
The text which triggers the method is drop
or discard
, so long as it’s followed by the name of
any item in the inventory location of the drama object’s WorldBuilder
.
def do_drop(
self, this, text, director, *args, item: "world.statewise[Spot.inventory]", **kwargs
):
"""
drop {item.names[0]}
discard {item.names[0]}
"""
item.set_state(self.here)
item.aspect = f"It lies on the floor."
yield Prologue(f"<> You drop the {item.names[0]}.")