earwax.mapping package

Module contents

Mapping functions and classes for Earwax.

This module is inspired by Camlorn’s post at this link.

All credit goes to him for the idea.

class earwax.mapping.Box(game: Game, start: earwax.point.Point, end: earwax.point.Point, name: Optional[str] = None, surface_sound: Optional[pathlib.Path] = None, wall_sound: Optional[pathlib.Path] = None, type: earwax.mapping.box.BoxTypes = NOTHING, data: Optional[T] = None, stationary: bool = NOTHING, reverb: Optional[object] = NOTHING, box_level: Optional[BoxLevel] = None)

Bases: typing.Generic, earwax.mixins.RegisterEventMixin

A box on a map.

You can create instances of this class either singly, or by using the earwax.Box.create_row() method.

If you already have a list of boxes, you can fit them all onto one map with the earwax.Box.create_fitted() method.

Boxes can be assigned arbitrary user data:

b: Box[Enemy] = Box(start, end, data=Enemy())
b.data.do_something()

In addition to the coordinates supplied to this class’s constructor, a earwax.BoxBounds instance is created as earwax.Box.bounds.

This class uses the pyglet.event framework, so you can register and dispatch events in the same way you would with pyglet.window.Window, or any other EventDispatcher subclass.

Variables:
  • game – The game that this box will work with.
  • start – The coordinates at the bottom rear left corner of this box.
  • end – The coordinates at the top front right corner of this box.
  • name – An optional name for this box.
  • surface_sound – The sound that should be heard when walking in this box.
  • wall_sound – The sound that should be heard when colliding with walls in this box.
  • type – The type of this box.
  • data – Arbitrary data for this box.
  • bounds – The bounds of this box.
  • centre – The point that lies at the centre of this box.
  • reverb – The reverb that is assigned to this box.
close() → None

Close the attached door.

If this box is a door, set the open attribute of its data to False, and play the appropriate sound. Otherwise, raise earwax.NotADoor.

Parameters:door – The door to close.
contains_point(coordinates: earwax.point.Point) → bool

Return whether or not this box contains the given point.

Returns True if this box spans the given coordinates, False otherwise.

Parameters:coordinates – The coordinates to check.
could_fit(box: earwax.mapping.box.Box) → bool

Return whether or not the given box could be contained by this one.

Returns True if the given box could be contained by this box, False otherwise.

This method behaves like the contains_point() method, except that it works with Box instances, rather than Point instances.

This method simply checks that the start and end points would fit inside this box.

Parameters:box – The box whose bounds will be checked.
classmethod create_fitted(game: Game, children: List[Box], pad_start: Optional[earwax.point.Point] = None, pad_end: Optional[earwax.point.Point] = None, **kwargs) → BoxType

Return a box that fits all of children inside itself.

Pass a list of Box instances, and you’ll get a box with its start, and end attributes set to match the outer bounds of the provided children.

You can use pad_start, and pad_end to add or subtract from the calculated start and end coordinates.

Parameters:
  • children – The list of Box instances to encapsulate.
  • pad_start – A point to add to the calculated start coordinates.
  • pad_end – A point to add to the calculated end coordinates.
  • kwargs – The extra keyword arguments to pass to Box.__init__.
classmethod create_row(game: Game, start: earwax.point.Point, size: earwax.point.Point, count: int, offset: earwax.point.Point, get_name: Optional[Callable[[int], str]] = None, on_create: Optional[Callable[[Box], None]] = None, **kwargs) → List[BoxType]

Generate a list of boxes.

This method is useful for creating rows of buildings, or rooms on a corridor to name a couple of examples.

It can be used like so:

offices = Box.create_row(
    game,  # Every Box instance needs a game.
    Point(0, 0),  # The bottom_left corner of the first box.
    Point(3, 2, 0),  # The size of each box.
    3,  # The number of boxes to build.
    # The next argument is how far to move from the top right
    # corner of each created box:
    Point(1, 0, 0),
    # We want to name each room. For that, there is a function!
    get_name=lambda i: f'Room {i + 1}',
    # Let's make them all rooms.
    type=RoomTypes.room
)

This will result in a list containing 3 rooms:

  • The first from (0, 0, 0) to (2, 1, 0)
  • The second from (3, 0, 0) to (5, 1, 0)
  • And the third from (6, 0, 0) to (8, 1, 0)

PLEASE NOTE: If none of the size coordinates are >= 1, the top right coordinate will be less than the bottom left, so get_containing_box() won’t ever find it.

Parameters:
  • start – The start coordinate of the first box.
  • size – The size of each box.
  • count – The number of boxes to build.
  • offset

    The distance between the boxes.

    If no coordinate of the given value is >= 1, overlaps will occur.

  • get_name

    A function which should return an appropriate name.

    This function will be called with the current position in the loop.

    0 for the first room, 1 for the second, and so on.

  • on_create

    A function which will be called after each box is created.

    The only provided argument will be the box that was just created.

  • kwargs – Extra keyword arguments to be passed to Box.__init__.
get_nearest_point(point: earwax.point.Point) → earwax.point.Point

Return the point on this box nearest to the provided point.

Parameters:point – The point to start from.
handle_door() → None

Open or close the door attached to this box.

handle_portal() → None

Activate a portal attached to this box.

is_door

Return True if this box is a door.

is_portal

Return True if this box is a portal.

is_wall(p: earwax.point.Point) → bool

Return True if the provided point is inside a wall.

Parameters:p – The point to interrogate.
classmethod maze(game: Game, grid: ndarray, box_height: int = 3) → Generator[Box, None, None]

Return a generator containing a list of boxes.

This constructor supports mazes generated by mazelib for example.

on_activate() → None

Handle the enter key.

This event is dispatched when the player presses the enter key.

It is guaranteed that the instance this event is dispatched on is the one the player is stood on.

on_close() → None

Handle this box being closed.

on_collide(coordinates: earwax.point.Point) → None

Play an appropriate wall sound.

This function will be called by the Pyglet event framework, and should be called when a player collides with this box.

on_footstep(bearing: float, coordinates: earwax.point.Point) → None

Play an appropriate surface sound.

This function will be called by the Pyglet event framework, and should be called when a player is walking on this box.

This event is dispatched by earwax.BoxLevel.move upon a successful move.

Parameters:coordinates – The coordinates the player has just moved to.
on_open() → None

Handle this box being opened.

open() → None

Open the attached door.

If this box is a door, set the open attribute of its data to True, and play the appropriate sound. Otherwise, raise earwax.NotADoor.

Parameters:box – The box to open.
scheduled_close(dt: float) → None

Call close().

This method will be called by pyglet.clock.schedule_once.

Parameters:dt – The dt parameter expected by Pyglet’s schedule functions.
sound_manager

Return a suitable sound manager.

class earwax.mapping.BoxBounds(bottom_back_left: earwax.point.Point, top_front_right: earwax.point.Point)

Bases: object

Bounds for a earwax.Box instance.

Variables:
  • bottom_back_left – The bottom back left point.
  • top_front_right – The top front right point.
  • bottom_front_left – The bottom front left point.
  • bottom_front_right – The bottom front right point.
  • bottom_back_right – The bottom back right point.
  • top_back_left – The top back left point.
  • top_front_left – The top front left point.
  • top_back_right – The top back right point.
area

Return the area of the box.

depth

Get the depth of this box (front to back).

height

Return the height of this box.

is_edge(p: earwax.point.Point) → bool

Return True if p represents an edge.

Parameters:p – The point to interrogate.
volume

Return the volume of this box.

width

Return the width of this box.

class earwax.mapping.BoxTypes

Bases: enum.Enum

The type of a box.

Variables:
  • empty

    Empty space.

    Boxes of this type can be traversed wit no barriers.

  • room

    An open room with walls around the edge.

    Boxes of this type can be entered by means of a door. The programmer must provide some means of exit.

  • solid

    Signifies a solid, impassible barrier.

    Boxes of this type cannot be traversed.

empty = 0
room = 1
solid = 2
exception earwax.mapping.NotADoor

Bases: earwax.mapping.box.BoxError

The current box is not a door.

exception earwax.mapping.NotAPortal

Bases: earwax.mapping.box.BoxError

The current box is not a portal.

class earwax.mapping.BoxLevel(game: Game, boxes: List[earwax.mapping.box.Box[typing.Any][Any]] = NOTHING, coordinates: earwax.point.Point = NOTHING, bearing: int = 0, current_box: Optional[earwax.mapping.box_level.CurrentBox] = None)

Bases: earwax.level.Level

A level that deals with sound generation for boxes.

This level can be used in your games. Simply bind the various action methods (listed below) to whatever triggers suit your purposes.

Some of the attributes of this class refer to a “perspective”. This could theoretically be anything you want, but most likely refers to the player. Possible exceptions include if you made an instance to represent some kind of long range vision for the player.

Action-ready Methods

  • move().
  • show_coordinates()
  • show_facing()
  • turn()
  • show_nearest_door()
  • describe_current_box()
Variables:
  • box – The box that this level will work with.
  • coordinates – The coordinates of the perspective.
  • bearing – The direction the perspective is facing.
  • current_box

    The most recently walked over box.

    If you don’t set this attribute when creating the instance, then the first time the player moves using the move() method, the name of the box they are standing on will be spoken.

  • reverb

    An optional reverb to play sounds through.

    You shouldn’t write to this property, instead use the connect_reverb() method to set a new reverb, and disconnect_reverb() to clear.

activate(door_distance: float = 2.0) → Callable[[], None]

Return a function that can be call when the enter key is pressed.

First we check if the current box is a portal. If it is, then we call handle_portal().

If it is not, we check to see if there is a door close enough to be opened or closed. If there is, then we call handle_door() on it.

If none of this works, and there is a current box, dispatch the on_activate() event to let the box do its own thing.

Parameters:door_distance – How close doors have to be for this method to open or close them.
add_box(box: earwax.mapping.box.Box[typing.Any][Any]) → None

Add a box to self.boxes.

Parameters:box – The box to add.
add_boxes(boxes: Iterable[earwax.mapping.box.Box]) → None

Add multiple boxes with one call.

Parameters:boxes – An iterable for boxes to add.
add_default_actions() → None

Add some default actions.

This method adds the following actions:

  • Move forward: W
  • Turn 180 degrees: S
  • Turn 45 degrees left: A
  • Turn 45 degrees right: D
  • Show coordinates: C
  • Show the facing direction: F
  • Describe current box: X
  • Speak nearest door: Z
  • Activate nearby objects: Return
calculate_coordinates(distance: float, bearing: int) → Tuple[float, float]

Calculate coordinates at the given distance in the given direction.

Used by move() to calculate new coordinates.

Override this method if you want to change the algorithm used to calculate the target coordinates.

Please bear in mind however, that the coordinates this method returns should always be 2d.

Parameters:
  • distance – The distance which should be used.
  • bearing

    The bearing the new coordinates are in.

    This value may not be the same as self.bearing.

collide(box: earwax.mapping.box.Box[typing.Any][Any], coordinates: earwax.point.Point) → None

Handle collitions.

Called to run collision code on a box.

Parameters:
  • box – The box the player collided with.
  • coordinates – The coordinates the player was trying to reach.
describe_current_box() → None

Describe the current box.

get_angle_between(other: earwax.point.Point) → float

Return the angle between the perspective and the other coordinates.

This function takes into account self.bearing.

Parameters:other – The target coordinates.
get_boxes(t: Any) → List[earwax.mapping.box.Box]

Return a list of boxes of the current type.

If no boxes are found, an empty list is returned.

Parameters:t – The type of the boxes.
get_containing_box(coordinates: earwax.point.Point) → Optional[earwax.mapping.box.Box]

Return the box that spans the given coordinates.

If no box is found, None will be returned.

This method scans self.boxes using the sort_boxes() method.

Parameters:coordinates – The coordinates the box should span.
get_current_box() → Optional[earwax.mapping.box.Box]

Get the box that lies at the current coordinates.

handle_box(box: earwax.mapping.box.Box[typing.Any][Any]) → None

Handle a bulk standard box.

The coordinates have already been set, and the on_footstep event dispatched, so all that is left is to speak the name of the new box, if it is different to the last one, update self.reverb if necessary, and store the new box.

move(distance: float = 1.0, vertical: Optional[float] = None, bearing: Optional[int] = None) → Callable[[], None]

Return a callable that allows the player to move on the map.

If the move is successful (I.E.: There is a box at the destination coordinates), the on_move() event is dispatched.

If not, then on_move_fail() is dispatched.

Parameters:
  • distance – The distance to move.
  • vertical – An optional adjustment to be added to the vertical position.
  • bearing

    An optional direction to move in.

    If this value is None, then self.bearing will be used.

nearest_by_type(start: earwax.point.Point, data_type: Any, same_z: bool = True) → Optional[earwax.mapping.box_level.NearestBox]

Get the nearest box to the given point by type.

If no boxes of the given type are found, None will be returned.

Parameters:
  • start – The point to start looking from.
  • data_type – The type of box data to search for.
  • same_z – If this value is True, only boxes on the same z axis will be considered.
nearest_door(start: earwax.point.Point, same_z: bool = True) → Optional[earwax.mapping.box_level.NearestBox]

Get the nearest door.

Iterates over all doors, and returned the nearest one.

Parameters:
  • start – The coordinates to start from.
  • same_z – If True, then doors on different levels will not be considered.
nearest_portal(start: earwax.point.Point, same_z: bool = True) → Optional[earwax.mapping.box_level.NearestBox]

Return the nearest portal.

Parameters:
  • start – The coordinates to start from.
  • same_z – If True, then portals on different levels will not be considered.
on_move_fail(distance: float, vertical: Optional[float], bearing: int, coordinates: earwax.point.Point) → None

Handle a move failure.

An event that will be dispatched when the move() action has been used, but no move was performed.

Parameters:
  • distance – The distance value that was passed to move().
  • vertical – The vertical value that was passed to move.
  • bearing – The bearing argument that was passed to move, or self.bearing.
on_move_success() → None

Handle a successful move.

An event that will be dispatched when the move() action is used.

By default, this method plays the correct footstep sound.

on_push() → None

Set listener orientation, and start ambiances and tracks.

on_turn() → None

Handle turning.

An event that will dispatched when the turn() action is used.

register_box(box: earwax.mapping.box.Box) → None

Register a box that is already in the boxes list.

Parameters:box – The box to register.
remove_box(box: earwax.mapping.box.Box[typing.Any][Any]) → None

Remove a box from self.boxes.

Parameters:box – The box to remove.
set_bearing(angle: int) → None

Set the direction of travel and the listener’s orientation.

Parameters:angle – The bearing (in degrees).
set_coordinates(p: earwax.point.Point) → None

Set the current coordinates.

Also set listener position.

Parameters:p – The new point to assign to self.coordinates.
show_coordinates(include_z: bool = False) → Callable[[], None]

Speak the current coordinates.

show_facing(include_angle: bool = True) → Callable[[], None]

Return a function that will let you see the current bearing as text.

For example:

l = BoxLevel(...)
l.action('Show facing', symbol=key.F)(l.show_facing())
Parameters:include_angle – If True, then the actual angle will be shown along with the direction name.
show_nearest_door(max_distance: Optional[float] = None) → Callable[[], None]

Return a callable that will speak the position of the nearest door.

Parameters:max_distance

The maximum distance between the current coordinates and the nearest door where the door will still be reported.

If this value is None, then any door will be reported.

sort_boxes() → List[earwax.mapping.box.Box]

Return children sorted by area.

turn(amount: int) → Callable[[], None]

Return a turn function.

Return a function that will turn the perspective by the given amount and dispatch the on_turn event.

For example:

l = BoxLevel(...)
l.action('Turn right', symbol=key.D)(l.turn(45))
l.action('Turn left', symbol=key.A)(l.turn(-45))

The resulting angle will always be in the range 0-359.

Parameters:amount

The amount to turn by.

Positive numbers turn clockwise, while negative numbers turn anticlockwise.

walls_between(end: earwax.point.Point, start: Optional[earwax.point.Point] = None) → int

Return the number of walls between two points.

Parameters:
  • end – The target coordinates.
  • start

    The coordinates to start at.

    If this value is None, then the current coordinates will be used.

class earwax.mapping.CurrentBox(coordinates: earwax.point.Point, box: earwax.mapping.box.Box[typing.Any][Any])

Bases: object

Store a reference to the current box.

This class stores the position too, so that caching can be performed.

Variables:
  • coordinates – The coordinates that were last checked.
  • box – The last current box.
class earwax.mapping.NearestBox(box: earwax.mapping.box.Box, coordinates: earwax.point.Point, distance: float)

Bases: object

A reference to the nearest box.

Variables:
  • box – The box that was found.
  • coordinates – The nearest coordinates to the ones specified.
  • distance – The distance between the supplied coordinates, and coordinates.
class earwax.mapping.Door(open: bool = True, closed_sound: Optional[pathlib.Path] = None, open_sound: Optional[pathlib.Path] = None, close_sound: Optional[pathlib.Path] = None, close_after: Union[float, Tuple[float, float], None] = None, can_open: Optional[Callable[[], bool]] = None, can_close: Optional[Callable[[], bool]] = None)

Bases: object

An object that can be added to a box to optionally block travel.

Doors can currently either be open or closed. When opened, they can optionally close after a specified time:

Door()  # Standard open door.
Door(open=False)  # Closed door.
Door(close_after=5.0)  # Will automatically close after 5 seconds.
# A door that will automatically close between 5 and 10 seconds after
# it has been opened:
Door(close_after=(5.0, 10.0)
Variables:
  • open

    Whether or not this box can be walked on.

    If this value is False, then the player will hear closed_sound when trying to walk on this box.

    If this value is True, the player will be able to enter the box as normal.

  • closed_sound – The sound that will be heard if open is False.
  • open_sound – The sound that will be heard when opening this door.
  • close_sound – The sound that will be heard when closing this door.
  • close_after

    When (if ever) to close the door after it has been opened.

    This attribute supports 3 possible values:

    • None: The door will not close on its own.
    • A tuple of two positive floats a and b: A random number
      between a and b will be selected, and the door will automatically close after that time.
    • A float: The exact time the door will automatically close after.
  • can_open

    An optional method which will be used to decide whether or not this door can be opened at this time.

    This method must return True or False, and must handle any messages which should be sent to the player.

  • can_close

    An optional method which will be used to decide whether or not this door can be closed at this time.

    This method must return True or False, and must handle any messages which should be sent to the player.

class earwax.mapping.MapEditor(game: Game, boxes: List[earwax.mapping.box.Box[typing.Any][Any]] = NOTHING, coordinates: earwax.point.Point = NOTHING, bearing: int = 0, current_box: Optional[earwax.mapping.box_level.CurrentBox] = None, filename: Optional[pathlib.Path] = None, context: earwax.mapping.map_editor.MapEditorContext = NOTHING)

Bases: earwax.mapping.box_level.BoxLevel

A level which can be used for editing maps.

When this level talks about a map, it talks about a earwax.mapping.map_editor.LevelMap instance.

box_menu(box: earwax.mapping.map_editor.MapEditorBox) → None

Push a menu to configure the provided box.

box_sound(template: earwax.mapping.map_editor.BoxTemplate, name: str) → Callable[[], Generator[None, None, None]]

Push an editor for setting the given sound.

Parameters:
  • template – The template to modify.
  • name – The name of the sound to modify.
box_sounds() → None

Push a menu for configuring sounds.

boxes_menu() → None

Push a menu to select a box to configure.

If there is only 1 box, it will not be shown.

complain_box() → None

Complain about there being no box.

create_box() → None

Create a box, then call box_menu().

get_default_context() → earwax.mapping.map_editor.MapEditorContext

Return a suitable context.

id_box() → Generator[None, None, None]

Change the ID for the current box.

label_box() → Generator[None, None, None]

Rename the current box.

on_move_fail(distance: float, vertical: Optional[float], bearing: int, coordinates: earwax.point.Point) → None

Tell the user their move failed.

point_menu(template: earwax.mapping.map_editor.BoxTemplate, point: earwax.mapping.map_editor.BoxPoint) → Callable[[], None]

Push a menu for configuring individual points.

points_menu() → None

Push a menu for moving the current box.

rename_box() → Generator[None, None, None]

Rename the current box.

save() → None

Save the map level.

class earwax.mapping.MapEditorContext(level: MapEditor, level_map: earwax.mapping.map_editor.LevelMap, template_ids: Dict[str, earwax.mapping.map_editor.BoxTemplate] = NOTHING, box_ids: Dict[str, earwax.mapping.box.Box[str][str]] = NOTHING)

Bases: object

A context to hold map information.

This class acts as an interface between a LevelMap instance, and a MapEditor instance.

add_template(template: earwax.mapping.map_editor.BoxTemplate, box: Optional[earwax.mapping.map_editor.MapEditorBox] = None) → None

Add a template to this context.

This method will add the given template to its box_template_ids dictionary.

Parameters:template – The template to add.
reload_template(template: earwax.mapping.map_editor.BoxTemplate) → None

Reload the given template.

This method recreates the box associated with the given template.

Parameters:template – The template to reload.
to_box(template: earwax.mapping.map_editor.BoxTemplate) → earwax.mapping.map_editor.MapEditorBox

Return a box from a template.

Parameters:template – The template to convert.
to_point(data: earwax.mapping.map_editor.BoxPoint) → earwax.point.Point

Return a point from the given data.

Parameters:data – The BoxPoint to load the point from.
class earwax.mapping.Portal(level: BoxLevel, coordinates: earwax.point.Point, bearing: Optional[int] = None, enter_sound: Optional[pathlib.Path] = None, exit_sound: Optional[pathlib.Path] = None, can_use: Optional[Callable[[], bool]] = None)

Bases: earwax.mixins.RegisterEventMixin

A portal to another map.

An object that can be added to a earwax.Box to make a link between two maps.

This class implements pyglet.event.EventDispatcher, so events can be registered and dispatched on it.

The currently-registered events are:

  • on_enter()
  • on_exit()
Variables:
  • level – The destination level.
  • coordinates – The exit coordinates.
  • bearing – If this value is None, then it will be used for the player’s bearing after this portal is used. Otherwise, the bearing from the old level will be used.
  • enter_sound

    The sound that should play when entering this portal.

    This sound is probably only used when an NPC uses the portal.

  • exit_sound

    The sound that should play when exiting this portal.

    This is the sound that the player will hear when using the portal.

  • can_use

    An optional method which will be called to ensure that this portal can be used at this time.

    This function should return True or False, and should handle any messages which should be sent to the player.

on_enter() → None

Handle a player entering this portal.

on_exit() → None

Handle a player exiting this portal.