Beispiel #1
0
 def __init__(self,
              game,
              name,
              description,
              aliases=None,
              traits=None,
              size=0,
              value=0,
              capacity=0):
     self.traits = Traits.merge(self, traits, Container.DEFAULT_TRAITS)
     super(Container, self).__init__(game,
                                     name,
                                     description,
                                     aliases,
                                     size=size,
                                     value=value)
     self.vocab = game.vocabulary
     self.put_preposition = 'in'
     self.capacity = capacity
     self.items = []
     self.add_patient_role('put')
     self.add_patient_role('get')
     if not self.traits.surface:
         self.add_patient_role('open')
         self.add_patient_role('close')
         self.add_patient_role('lock')
         self.add_patient_role('unlock')
Beispiel #2
0
 def __init__(self, game, name, description, aliases=None, traits=None):
     self.traits = Traits.merge(self, traits, Door.DEFAULT_TRAITS)
     super(Door, self).__init__(game, name, description, aliases)
     self.add_patient_role('open')
     self.add_patient_role('close')
     self.add_patient_role('lock')
     self.add_patient_role('unlock')
Beispiel #3
0
 def __init__(self, game, name, description, aliases=None, traits=None, size=0, value=0):
     traits = Traits.merge(self, traits, Location.DEFAULT_TRAITS)
     super(Location, self).__init__(game, name, description, aliases=aliases, traits=traits,
                                    size=size, value=value, capacity=Container.MAX_CAPACITY)
     self.vocab = game.vocabulary
     self.exits = dict()
     self.visited = False
Beispiel #4
0
 def __init__(self,
              game,
              name,
              description,
              aliases=None,
              traits=None,
              health=0,
              strength=0,
              dexterity=0,
              speed=50,
              activity=33,
              location=None,
              entry_action=None,
              present_action=None,
              exit_action=None):
     self.traits = Traits.merge(self, traits, Creature.DEFAULT_TRAITS)
     super(Creature, self).__init__(game, name, description, aliases)
     self.health = health
     self.location = location
     self.strength = strength
     self.dexterity = dexterity
     self.last_movement = 0
     self.movement_frequency = int(100 / speed)
     self.movement_strategy = self.random_walk
     self.last_activity = 0
     self.activity_frequency = int(100 / activity)
     self.wanted_items = []
     if not entry_action:
         self.entry_action = self.default_entry
     if not exit_action:
         self.exit_action = self.default_exit
     if not present_action:
         self.present_action = self.neutral_action
     if self.location:
         self.location.add_item(self)
     self.inventory = Container(game,
                                name + '-inv',
                                name + "'s items",
                                traits=Traits(closed=False),
                                capacity=strength)
     self.add_patient_role('greet')
     self.add_patient_role('wave')
     self.add_patient_role('smile')
     self.add_patient_role('give')
     self.add_patient_role('ask')
     self.add_patient_role('kill')
     self.add_patient_role('take')
Beispiel #5
0
 def __init__(self, game, location=None):
     self.traits = Traits.merge(self, None, Player.DEFAULT_TRAITS)
     super(Player,
           self).__init__(game=game,
                          name='me',
                          description='A travel-weary but brave adventurer',
                          aliases=None,
                          health=100,
                          strength=75,
                          dexterity=80,
                          location=location)
     self.game = game
     self.vocab = game.vocabulary
Beispiel #6
0
class VisibleThing(Thing):

    DEFAULT_TRAITS = Traits(visible=True)

    def __init__(self, game, name, description, aliases=None, traits=None):
        self.traits = Traits.merge(self, traits, VisibleThing.DEFAULT_TRAITS)
        super(VisibleThing, self).__init__(game, name, aliases)
        self.description = description
        self.add_patient_role('look')
        self.add_theme_role('throw')

    def describe(self):
        return Success(self.description)
Beispiel #7
0
class Door(Item):

    DEFAULT_TRAITS = Traits(portable=False,
                            evident=False,
                            closed=True,
                            locked=True)

    def __init__(self, game, name, description, aliases=None, traits=None):
        self.traits = Traits.merge(self, traits, Door.DEFAULT_TRAITS)
        super(Door, self).__init__(game, name, description, aliases)
        self.add_patient_role('open')
        self.add_patient_role('close')
        self.add_patient_role('lock')
        self.add_patient_role('unlock')
Beispiel #8
0
 def __init__(self, game, name, location, direction, destination, description,
              condition, fail_result, after=None, traits=None):
     self.traits = Traits.merge(self, traits, Passage.DEFAULT_TRAITS)
     super(Passage, self).__init__(game, name, aliases=None)
     self.vocab = game.vocabulary
     self.location = location.id
     self.direction = direction.id
     self.destination = destination.id
     self.description = description
     self.condition = condition
     self.after = after
     if fail_result:
         self.fail_result = fail_result
     else:
         self.fail_result = Passage.DEFAULT_FAIL_RESULT
Beispiel #9
0
class Key(Item):

    DEFAULT_TRAITS = Traits()

    def __init__(self, game, name, description, aliases=None, traits=None):
        self.traits = Traits.merge(self, traits, Key.DEFAULT_TRAITS)
        super(Key, self).__init__(game, name, description, aliases)
        self.add_instrument_role('lock')
        self.add_instrument_role('unlock')
        self.key_for = set()

    def set_lockable(self, lockable):
        self.key_for.add(lockable.id)

    def can_lock(self, lockable):
        return lockable.id in self.key_for
Beispiel #10
0
 def __init__(self,
              game,
              name,
              description,
              aliases=None,
              traits=None,
              size=0,
              value=0,
              capacity=MAX_CAPACITY):
     self.traits = Traits.merge(self, traits, Surface.DEFAULT_TRAITS)
     super(Surface, self).__init__(game,
                                   name,
                                   description,
                                   aliases,
                                   traits,
                                   size=size,
                                   value=value,
                                   capacity=capacity)
     self.put_preposition = 'on'
Beispiel #11
0
    def __init__(self, game, name, aliases=None, traits=None):

        self.game = game
        self.vocabulary = game.vocabulary
        self.id = self.vocabulary.register_noun(self)
        self.name = name
        if aliases:
            self.aliases = aliases
        else:
            self.aliases = []
        self.traits = Traits.merge(self, traits, Thing.DEFAULT_TRAITS)
        self.modifiers = set()
        self.valid_roles = {
            Role.AGENT: [],
            Role.PATIENT: [],
            Role.THEME: [],
            Role.GOAL: [],
            Role.INSTRUMENT: []
        }
Beispiel #12
0
class Direction(VisibleThing):

    DEFAULT_TRAITS = Traits(ubiquitous=True)

    @classmethod
    def create(cls, game, name, alias):
        d = Direction(game, name)
        Action.create(game=game,
                      name=name,
                      aliases=[alias],
                      callback=lambda schema: schema[Role.AGENT].go(d))
        return d

    def __init__(self, game, name, description=None, aliases=None):
        if description is None:
            description = 'An exit leading ' + name
        self.traits = Traits.merge(self, None, Direction.DEFAULT_TRAITS)
        super(Direction, self).__init__(game, name, description, aliases)
        self.add_goal_role('go')
Beispiel #13
0
 def __init__(self,
              game,
              name,
              description,
              aliases=None,
              traits=None,
              size=0,
              value=0,
              damage=10,
              defense=10,
              accuracy=50):
     self.traits = Traits.merge(self, traits, Weapon.DEFAULT_TRAITS)
     super(Weapon, self).__init__(game,
                                  name,
                                  description,
                                  aliases,
                                  size=size,
                                  value=value)
     self.add_instrument_role('hit')
     self.damage = damage
     self.defense = defense
     self.accuracy = accuracy
Beispiel #14
0
class Surface(Container):

    MAX_CAPACITY = Container.MAX_CAPACITY
    DEFAULT_TRAITS = Traits(closed=False, portable=False, surface=True)

    def __init__(self,
                 game,
                 name,
                 description,
                 aliases=None,
                 traits=None,
                 size=0,
                 value=0,
                 capacity=MAX_CAPACITY):
        self.traits = Traits.merge(self, traits, Surface.DEFAULT_TRAITS)
        super(Surface, self).__init__(game,
                                      name,
                                      description,
                                      aliases,
                                      traits,
                                      size=size,
                                      value=value,
                                      capacity=capacity)
        self.put_preposition = 'on'

    def append_container_description(self, description):
        item_count = len(self.items)
        if item_count == 1:
            item = self.vocab.lookup_noun(self.items[0])
            description.append(", with {} {} on it".format(
                item.article(), item.full_name()))
        elif item_count > 1:
            description.append("\nOn the {} are: ".format(self.full_name()))
            for item_id in self.items:
                item = self.vocab.lookup_noun(item_id)
                description.append("\n\t" + item.article().capitalize() + " " +
                                   item.full_name())
        return description
Beispiel #15
0
 def __init__(self, game, name, description, aliases=None, traits=None):
     self.traits = Traits.merge(self, traits, VisibleThing.DEFAULT_TRAITS)
     super(VisibleThing, self).__init__(game, name, aliases)
     self.description = description
     self.add_patient_role('look')
     self.add_theme_role('throw')
Beispiel #16
0
class Location(Container):

    DEFAULT_TRAITS = Traits(closed=False)

    def __init__(self, game, name, description, aliases=None, traits=None, size=0, value=0):
        traits = Traits.merge(self, traits, Location.DEFAULT_TRAITS)
        super(Location, self).__init__(game, name, description, aliases=aliases, traits=traits,
                                       size=size, value=value, capacity=Container.MAX_CAPACITY)
        self.vocab = game.vocabulary
        self.exits = dict()
        self.visited = False

    def to_json(self):
        return vars(self)

    @staticmethod
    def free_passage(game, player):
        return True

    def add_exit(self, direction, destination, description=None, condition=None, fail_result=None, after=None):
        if not condition:
            condition = Location.free_passage
        passage = Passage(self.game, self.name + '-' + direction.name,
                          self, direction, destination, description, condition, fail_result, after)
        self.exits[direction.id] = passage

    def available_exits(self, game):
        return [self.exits[d] for d in self.exits if self.exits[d].condition(game, game.player)]

    def list_nondescript_exits(self):
        return ', '.join([self.vocab.lookup_noun(x).name for x in self.exits.keys() if not self.exits[x].description])

    def append_exit_description(self, description):
        special_exits = [x for x in self.exits.keys() if self.exits[x].description]
        nondescript_exits = [x for x in self.exits.keys() if not self.exits[x].description]
        description.append("\n")
        for x in special_exits:
            description.append("\n" + self.exits[x].description)
        if len(special_exits) > 0:
            if len(nondescript_exits) == 1:
                description.append("\nAnother exit leads " + self.list_nondescript_exits())
            elif len(nondescript_exits) > 1:
                description.append("\nOther exits lead " + self.list_nondescript_exits())
        else:
            if len(nondescript_exits) == 1:
                description.append("\nAn exit leads " + self.list_nondescript_exits())
            elif len(nondescript_exits) > 1:
                description.append("\nExits lead " + self.list_nondescript_exits())

    def append_compelling_items(self, description):
        for item_id in self.items:
            item = self.vocab.lookup_noun(item_id)
            if item.traits.compelling:
                description.append("\n" + item.description)

    def append_evident_items(self, description):
        for item_id in self.items:
            item = self.vocab.lookup_noun(item_id)
            if item.traits.evident and not item.traits.compelling:
                description.append("\nThere {} {} {} here"
                                   .format(item.existential(), item.article(), item.full_name()))
                if isinstance(item, Container) and not item.traits.closed:
                    item.append_container_description(description)

    def describe(self, verbose=True):
        description = Result("\n------ " + self.name + " ------", success=True)
        if verbose or not self.visited:
            description.append("\n\n" + self.description)
        self.append_compelling_items(description)
        self.append_evident_items(description)
        self.append_exit_description(description)
        return description
Beispiel #17
0
 def __init__(self, game, name, description, aliases=None, traits=None):
     self.traits = Traits.merge(self, traits, Key.DEFAULT_TRAITS)
     super(Key, self).__init__(game, name, description, aliases)
     self.add_instrument_role('lock')
     self.add_instrument_role('unlock')
     self.key_for = set()
Beispiel #18
0
class Player(Creature):

    DEFAULT_TRAITS = Traits(evident=False)

    def __init__(self, game, location=None):
        self.traits = Traits.merge(self, None, Player.DEFAULT_TRAITS)
        super(Player,
              self).__init__(game=game,
                             name='me',
                             description='A travel-weary but brave adventurer',
                             aliases=None,
                             health=100,
                             strength=75,
                             dexterity=80,
                             location=location)
        self.game = game
        self.vocab = game.vocabulary

    def is_valid_for_role(self, role, attempted_action):
        if role == Role.AGENT:
            return True
        else:
            return super().is_valid_for_role(role, attempted_action)

    def in_current_location(self, target):
        return target.id in self.location.items

    def is_nearby(self, target):
        return self.in_inventory(target) or self.in_current_location(target)

    def nearby_container_having(self, target):
        found_container = None
        nearby_item_ids = self.inventory.items + self.location.items
        nearby_items = [
            self.game.vocabulary.lookup_noun(i) for i in nearby_item_ids
        ]
        nearby_containers = [
            c for c in nearby_items
            if isinstance(c, Container) and not c.traits.closed
        ]
        for c in nearby_containers:
            if target.id in c.items:
                found_container = c
                break
        return found_container

    def nearby_creature_having(self, target):
        found_creature = None
        nearby_item_ids = self.inventory.items + self.location.items
        nearby_items = [
            self.game.vocabulary.lookup_noun(i) for i in nearby_item_ids
        ]
        nearby_creatures = [
            c for c in nearby_items if isinstance(c, Creature) and c != self
        ]
        for c in nearby_creatures:
            if target.id in c.inventory.items:
                found_creature = c
                break
        return found_creature

    def go(self, direction):
        if direction.id in self.location.exits:
            self.location.visited = True
            passage = self.location.exits[direction.id]
            r1 = passage.go(self.game, self)
            if r1.success:
                r = self.location.describe(verbose=False)
            else:
                r = r1
        else:
            r = Failure("You can't go {} from here".format(direction.name))
        return r

    def get(self, item):
        if not item.traits.portable:
            r = Failure("You can't carry around the {}!".format(
                item.full_name()))
        elif self.in_inventory(item):
            r = Failure("You already have the {}!".format(item.full_name()))
        elif not self.is_nearby(item):
            container = self.nearby_container_having(item)
            creature = self.nearby_creature_having(item)
            if container:
                r = self.get_from(item, container)
            elif creature:
                r = self.ask_for(creature, item)
            else:
                r = ProximityFailure(item.full_name())
        else:
            r1 = self.location.remove_item(item)
            if r1.success:
                r2 = self.inventory.add_item(item)
                if r2.success:
                    r = Success("You take the " + item.full_name())
                else:
                    self.location.add_item(item)
                    r = Failure(
                        "Your load is too heavy to pick up the {}".format(
                            item.full_name()))
        return r

    def drop(self, item):
        r1 = self.inventory.remove_item(item)
        if r1.success:
            r2 = self.location.add_item(item)
            if r2.success:
                r = Success("You drop the " + item.full_name())
            else:
                r = Failure("You can't drop the " + item.full_name() + "here!")
        else:
            r = OwnershipFailure(item.full_name())
        return r

    def look(self, item=None):
        if item is None:
            item = self.location
            r = item.describe()
        elif self.is_nearby(item):
            r = item.describe()
        else:
            container = self.nearby_container_having(item)
            if container:
                r = item.describe()
            else:
                r = ProximityFailure(item.full_name())
        return r

    def open(self, openable):
        if self.is_nearby(openable):
            if openable.traits.closed:
                if openable.traits.locked:
                    r = Failure(
                        "You try to open the {}, but it is firmly locked".
                        format(openable.full_name()))
                else:
                    openable.traits.closed = False
                    if isinstance(openable, Container):
                        container = openable
                        item_count = container.item_count()
                        if item_count == 0:
                            r = Success(
                                "You open the {}, which is empty".format(
                                    container.full_name()))
                        elif item_count == 1:
                            r = Success("Opening the {} reveals a {}".format(
                                container.full_name(),
                                self.vocab.lookup_noun(
                                    container.items[0]).full_name()))
                        else:
                            r = Success("Opening the {} reveals: ".format(
                                container.full_name()))
                            for item_id in container.items:
                                item = self.vocab.lookup_noun(item_id)
                                r.append("\n\t" + item.description)
                    else:
                        r = Success("You open the {}".format(
                            openable.full_name()))
            else:
                r = Failure("The {} is already open".format(
                    openable.full_name()))
        else:
            r = ProximityFailure(openable.full_name())
        return r

    def close(self, closeable):
        if self.is_nearby(closeable):
            if closeable.traits.closed:
                r = Failure("The {} is already closed".format(
                    closeable.full_name()))
            else:
                closeable.traits.closed = True
                r = Success("You close the {}".format(closeable.full_name()))
        else:
            r = ProximityFailure(closeable.full_name())
        return r

    def lock_with(self, lockable, key):
        if self.is_nearby(lockable):
            if lockable.traits.locked:
                r = Failure("The {} is already locked".format(
                    lockable.full_name()))
            else:
                if key.can_lock(lockable):
                    lockable.traits.locked = True
                    r = Success("The {} is now locked".format(
                        lockable.full_name()))
                else:
                    r = Failure("The {} cannot be locked with the {}!".format(
                        lockable.full_name(), key.full_name()))
        else:
            r = ProximityFailure(lockable.full_name())
        return r

    def unlock_with(self, lockable, key):
        if self.is_nearby(lockable) or self.nearby_container_having(lockable):
            if not lockable.traits.locked:
                r = Failure("The {} is already unlocked".format(
                    lockable.full_name()))
            else:
                if key.can_lock(lockable):
                    lockable.traits.locked = False
                    r = Success("The {} is now unlocked".format(
                        lockable.full_name()))
                else:
                    r = Failure(
                        "The {} cannot be unlocked with the {}!".format(
                            lockable.full_name(), key.full_name()))
        else:
            r = ProximityFailure(lockable.full_name())
        return r

    def diagnose(self):
        return Success("Health: {}; strength: {}".format(
            self.health, self.strength))

    def list_inventory(self):
        if len(self.inventory.items) > 0:
            r = Success("You are carrying:")
            for item_id in self.inventory.items:
                item = self.vocab.lookup_noun(item_id)
                r.append("\n\t" + item.description)
        else:
            r = Success("You're not carrying anything")
        return r

    def put_in(self, item, container):
        if not self.is_nearby(container):
            r = ProximityFailure(container.full_name())
        else:
            if not self.is_nearby(item):
                r = ProximityFailure(item.full_name())
            else:
                if container == item:
                    r = Failure("You can't put the {} {} itself!".format(
                        container.full_name(), container.put_preposition))
                else:
                    if self.in_inventory(item):
                        source = self.inventory
                    else:
                        source = self.location
                    r1 = source.remove_item(item)
                    if r1.success:
                        r2 = container.add_item(item)
                        if r2.success:
                            r = Success("The {} {} now {} the {}".format(
                                item.full_name(),
                                item.existential(), container.put_preposition,
                                container.full_name()))
                        else:
                            source.add_item(item)
                            r = r2
                    else:
                        r = r1
        return r

    def get_from(self, item, container):
        if isinstance(container, Creature):
            r = self.ask_for(container, item)
        else:
            if not self.is_nearby(container):
                r = ProximityFailure(container.full_name())
            else:
                r1 = container.remove_item(item)
                if r1.success:
                    r2 = self.inventory.add_item(item)
                    if r2.success:
                        r = Success("You take the {} from the {}".format(
                            item.full_name(), container.full_name()))
                    else:
                        container.add_item(item)
                        r = r2
                else:
                    r = r1
        return r

    def consume(self, edible, adjective, verb):
        if not self.is_nearby(edible):
            r = ProximityFailure(edible.full_name())
        else:
            self.health += edible.healing
            if self.in_inventory(edible):
                edible.consume_from(self.inventory)
            else:
                edible.consume_from(self.location)
            r = Success("You {} the {} {}".format(verb, adjective,
                                                  edible.full_name()))
        return r

    def eat(self, edible):
        return self.consume(edible, 'delicious', 'eat')

    def drink(self, edible):
        return self.consume(edible, 'refreshing', 'drink')

    def greet(self, creature):
        if not self.is_nearby(creature):
            r = ProximityFailure(creature.full_name())
        else:
            r = creature.respond_to_greeting()
        return r

    def give_to(self, item, creature):
        if not self.is_nearby(creature):
            r = ProximityFailure(creature.full_name())
        elif not self.in_inventory(item):
            r = OwnershipFailure(item.full_name())
        elif item in creature.wanted_items:
            r1 = self.inventory.remove_item(item)
            if r1.success:
                r2 = creature.inventory.add_item(item)
                if r2.success:
                    creature.become_friendly()
                    r = Success("The {} gratefully accepts the {}".format(
                        creature.full_name(), item.full_name()))
                else:
                    self.inventory.add_item(item)
                    r = r2
            else:
                r = r1
        else:
            r = Failure("The {} politely declines to take the {}".format(
                creature.full_name(), item.full_name()))
        return r

    def ask_for(self, creature, item):
        if not self.is_nearby(creature):
            r = ProximityFailure(creature.full_name())
        elif not creature.in_inventory(item):
            r = Failure("The {} doesn't have the {}".format(
                creature.full_name(), item.full_name()))
        elif not creature.traits.friendly or item in creature.wanted_items:
            r = Failure("The {} is unwilling to part with the {}".format(
                creature.full_name(), item.full_name()))
        else:
            r1 = creature.inventory.remove_item(item)
            if r1.success:
                r2 = self.inventory.add_item(item)
                if r2.success:
                    r = Success(
                        "The {} gladly hands over the {} to you".format(
                            creature.full_name(), item.full_name()))
                else:
                    creature.inventory.add_item(item)
                    r = r2
            else:
                r = r1
        return r

    def hit_with(self, creature, weapon):
        if not self.is_nearby(creature):
            r = ProximityFailure(creature.full_name())
        elif not self.in_inventory(weapon):
            r = OwnershipFailure(weapon.full_name())
        else:
            if not creature.traits.hostile:
                creature.become_hostile()
            attack_difficulty = random.randint(0, 100)
            if weapon.accuracy < attack_difficulty:
                r = Failure("Your attack misses!")
            else:
                creature.health -= weapon.damage
                if creature.is_alive():
                    r = Success(
                        "You hit the {}. It reels from your attack!".format(
                            creature.full_name()))
                else:
                    r = Success((
                        "You deal the {} a fatal blow. It falls to the ground dead, "
                        +
                        "and its body dissolves into the hungry earth").format(
                            creature.full_name()))
                    for item_id in creature.inventory.items:
                        item = self.game.vocabulary.lookup_noun(item_id)
                        creature.inventory.remove_item(item)
                        self.location.add_item(item)
                    self.location.remove_item(creature, force=True)
                    creature.location = None
        return r

    def throw(self, throwable, target=None):
        if not self.in_inventory(throwable):
            r = OwnershipFailure(throwable.full_name())
        elif target and not self.is_nearby(target):
            r = ProximityFailure(throwable.full_name())
        else:
            if throwable.size > 30:
                message = "You heave the {} {}; it falls to the ground {}"
                direction = "into the air"
                result = "with a solid thunk"
            elif 10 < throwable.size <= 30:
                message = "You lob the {} {}; it hits the ground {}"
                direction = "across the room"
                result = "and rolls to a stop"
            else:  # throwable.size < 10
                message = "You toss the {} {}; it comes back down {}"
                direction = "into the air"
                result = "with a soft plop"

            if target:
                direction = "at the {}, but miss".format(target.full_name())

            if throwable.traits.fragile:
                result = "and smashes to bits"
                self.inventory.remove_item(throwable)
            else:
                self.drop(throwable)
            r = Failure(
                message.format(throwable.full_name(), direction, result))

        return r
Beispiel #19
0
    def setup_items(self, game):

        # --------------- Common effects of commands

        def update_score(game, points):
            game.score += points

        def update_health(player, points):
            player.health += points

        def make_creature_hostile(creature):
            creature.become_hostile()

        def instant_death(player):
            player.health = 0

        def lose_item(player, item):
            player.inventory.remove_item(item)

        def end_section(name, game, player, points):
            update_score(game, points)
            result = Result(
                "Congratulations! You have finished {}. ".format(name))
            status = game.status()
            result.append(status.get_message())
            return result

        # ---------------- Create basic locations and objects

        player = game.player

        crumbly_room = Location(
            game,
            name='Crumbly Room',
            description='A small storage room with crumbling plaster walls')
        paneled_room = Location(
            game,
            name='Wood Paneled Room',
            description='A large, warm room with wood-paneled walls')
        north_tower = Location(
            game,
            name='North Tower',
            description='A tower with a high ceiling and red-stained windows')
        balcony = Location(
            game,
            name='Balcony',
            description=
            'A breezy, open balcony with a beautiful view of the landscape below',
            traits=Traits(precarious=True))
        east_tower = Location(
            game,
            name='East Tower',
            description='The tall tower at the eastern side of the house')
        roof = Location(
            game,
            name='Roof',
            description='You are on the roof of the eastern tower.  ' +
            'There is a ladder leading back downwards, and to the west is an open window.',
            traits=Traits(precarious=True))

        west_tower = Location(
            game,
            name='West Tower',
            description='The tall tower at the western side of the house.')

        master_bedroom = Location(
            game,
            'Master Bedroom',
            description=
            'This appears to be a former bedroom, but the bed itself is missing.  '
            + 'A window to the east is open.')
        basement = Location(game,
                            'Basement',
                            description='An empty room, very drafty')
        garden = Location(game, 'garden', description='a lush blooming garden')
        patio = Location(game, 'patio', description='an empty stone patio')
        front_porch = Location(
            game,
            'porch',
            description=
            'The front porch of the house.  A metal gate prevents you from leaving'
        )
        front_porch.add_modifier('front')

        metal_gate = Surface(
            game,
            name='gate',
            description=
            'An impassable metal gate with two locks: one golden and one bronze, '
            + 'blocking your exit!')
        metal_gate.traits.closed = True
        metal_gate.add_modifier('metal')
        front_porch.add_item(metal_gate)

        golden_lock = Door(game, name='lock', description='a golden lock')
        golden_lock.add_modifier('golden')
        metal_gate.add_item(golden_lock, force=True)

        bronze_lock = Door(game, name='lock', description='a bronze lock')
        bronze_lock.add_modifier('bronze')
        metal_gate.add_item(bronze_lock, force=True)

        street = Location(game, 'street', description='an empty street')

        fancy_painting = Item(
            game,
            name='painting',
            description='An ornate painting of the house\'s previous owner',
            aliases=['portrait', 'picture'],
            size=15,
            value=100)
        fancy_painting.add_modifier('fancy')
        west_tower.add_item(fancy_painting)

        east_tower.add_exit(direction.west, paneled_room)
        crumbly_room.add_exit(direction.north, paneled_room)
        paneled_room.add_exit(direction.south, crumbly_room)
        paneled_room.add_exit(direction.north, north_tower)
        paneled_room.add_exit(direction.east, east_tower)
        paneled_room.add_exit(direction.west, west_tower)
        west_tower.add_exit(direction.east, paneled_room)
        roof.add_exit(direction.west, master_bedroom)
        master_bedroom.add_exit(direction.down, basement)
        master_bedroom.add_exit(direction.east, roof)
        basement.add_exit(direction.up, master_bedroom)
        basement.add_exit(direction.west, garden)
        garden.add_exit(direction.east, basement)
        garden.add_exit(direction.south, patio)
        patio.add_exit(direction.north, garden)
        patio.add_exit(direction.south, front_porch)

        front_porch.add_exit(direction.north, patio)
        front_porch.add_exit(
            direction.south,
            street,
            condition=lambda g, p: not metal_gate.traits.closed,
            after=lambda g, p, l, d: end_section("section one", g, p, 50),
            fail_result=Failure("The metal gate blocks your way"))

        def too_small_check(g, p):
            return p.inventory.used_capacity() <= 25

        too_small_result = Failure(
            "Your load is too large to fit through the small hole")

        east_tower.add_exit(
            direction.up,
            roof,
            description='A ladder leads up to a hole in the ceiling far above',
            condition=too_small_check,
            fail_result=too_small_result)
        roof.add_exit(
            direction.down,
            east_tower,
            description="There is a hole here leading down to the tower below",
            condition=too_small_check,
            fail_result=too_small_result)

        sturdy_door = Door(
            game,
            name='door',
            description=
            'A sturdy door leading out to the balcony above the tower')
        sturdy_door.add_modifier('sturdy')

        silver_key = Key(game,
                         name='key',
                         description='A brilliant silver key')
        silver_key.add_modifier('silver')

        steel_key = Key(game, name='key', description='A small steel key')
        steel_key.add_modifier('steel')
        steel_key.set_lockable(sturdy_door)
        roof.add_item(steel_key)

        north_tower.add_item(sturdy_door)
        north_tower.add_exit(direction.south, paneled_room)
        north_tower.add_exit(
            direction.up,
            balcony,
            description="Stairs lead up to a door high above",
            condition=lambda g, p: not sturdy_door.traits.closed,
            fail_result=Failure("A sturdy door blocks the way"))
        balcony.add_exit(direction.down, north_tower)

        light_thing = Item(
            game,
            name='thing',
            description='An easily carried thing, light as a feather',
            size=0)
        light_thing.add_modifier('light')

        fragile_thing = Item(game,
                             name='thing',
                             description='An easily breakable, delicate thing',
                             traits=Traits(fragile=True),
                             size=15)
        fragile_thing.add_modifier('fragile')

        heavy_thing = Item(
            game,
            name='thing',
            description='A heavy block of some unknown material',
            size=45)
        heavy_thing.add_modifier('heavy')

        trunk = Container(game,
                          name='trunk',
                          description='An old wooden trunk',
                          aliases=['chest', 'box'],
                          size=75,
                          value=5,
                          capacity=90)
        trunk.add_modifier('wooden')
        sword = Weapon(game,
                       name='sword',
                       description='A serviceable iron sword',
                       size=15,
                       value=15,
                       damage=50,
                       defense=10,
                       accuracy=80)
        sword.add_modifier('iron')
        trunk.add_item(sword, force=True)

        diamond = Item(game,
                       name='diamond',
                       aliases=['gem', 'jewel'],
                       size=5,
                       value=100,
                       description='A brilliant diamond')

        apple = Edible(game,
                       name='apple',
                       description='A delicious, juicy red apple',
                       size=15,
                       value=5)
        small_table = Surface(game,
                              name='table',
                              description='A small table',
                              size=20,
                              capacity=15)
        small_table.add_modifier('small')
        small_table.add_item(apple, force=True)

        large_table = Surface(game,
                              name='table',
                              description='A large table',
                              size=75,
                              capacity=100)
        large_table.add_modifier('large')
        large_table.add_item(heavy_thing, force=True)
        large_table.add_item(light_thing, force=True)
        large_table.add_item(fragile_thing, force=True)

        bread = Edible(game,
                       name='bread',
                       description='A loaf of crusty brown bread',
                       size=20,
                       value=5,
                       healing=10)

        puddle = Drinkable(game,
                           name='puddle',
                           aliases=['water'],
                           description='A puddle of refreshing water',
                           size=25,
                           value=5,
                           healing=15)

        mouse = Item(
            game,
            name='mouse',
            description='A small mouse scampers back and forth across the ' +
            'room here, searching for food.  It is carrying something '
            'shiny in its mouth.',
            traits=Traits(compelling=True),
            size=0,
            value=0)
        mouse.add_modifier('brown')
        west_tower.add_item(mouse)

        core = Item(game,
                    name='core',
                    description='The core of an eaten apple',
                    size=5,
                    value=0)
        core.add_modifier('apple')
        apple.add_consumed(core)

        crumbs = Edible(game,
                        name='crumbs',
                        description='A small pile of leftover bread crumbs',
                        aliases=['pile'],
                        traits=Traits(composite=True, plural=True),
                        size=5,
                        value=0,
                        healing=0)
        bread.add_consumed(crumbs)

        mud = Item(game,
                   name='mud',
                   description='A clump of soggy wet mud',
                   traits=Traits(composite=True),
                   size=15,
                   value=1)
        puddle.add_consumed(mud)
        vase = Container(game,
                         name='flowerpot',
                         description='a patterned flowerpot',
                         aliases=['flowerpot', 'pot', 'vase'],
                         traits=Traits(closed=False, fragile=True),
                         size=5,
                         value=10,
                         capacity=3)
        flower = Item(game,
                      name='flower',
                      description='a beautiful, fragrant sunflower',
                      size=3,
                      value=5)
        crumbly_room.add_item(small_table)
        crumbly_room.add_item(large_table)
        paneled_room.add_item(trunk)
        paneled_room.add_item(puddle)

        vase.add_item(flower)
        balcony.add_item(vase)

        villager = Creature(
            game,
            name='villager',
            traits=Traits(mobile=True),
            aliases=['farmer'],
            description="A stout but simple villager in farming garb",
            health=75,
            strength=25,
            dexterity=65,
            location=paneled_room)
        villager.add_wanted_item(apple)
        villager.add_item(diamond)

        golden_key = Key(game,
                         name='key',
                         description='A fashionable golden key')
        golden_key.add_modifier('golden')
        golden_key.set_lockable(golden_lock)

        red_fox = Creature(game,
                           name='fox',
                           traits=Traits(hostile=True, mobile=False),
                           description="a bloodthirsty red fox",
                           health=65,
                           strength=15,
                           dexterity=50,
                           location=front_porch)
        red_fox.add_modifier('red')
        red_fox.add_item(golden_key)

        bronze_key = Key(game, name='key', description='A dull bronze key')
        bronze_key.add_modifier('bronze')
        bronze_key.set_lockable(bronze_lock)

        brown_fox = Creature(game,
                             name='fox',
                             traits=Traits(hostile=True, mobile=False),
                             description="a vicious brown fox",
                             health=65,
                             strength=25,
                             dexterity=50,
                             location=front_porch)
        brown_fox.add_modifier('brown')
        red_fox.add_item(bronze_key)

        townsfolk = Creature(
            game,
            name='townsfolk',
            traits=Traits(mobile=True),
            description='A short, well-dressed man with a stubbly beard',
            aliases=['folk', 'man'],
            health=75,
            strength=30,
            dexterity=65,
            location=north_tower)
        townsfolk.add_wanted_item(diamond)
        townsfolk.add_item(bread)

        def shadow_action(game, player):
            player.health -= 5
            return Result("A dark shadow looms ominously in the corner")

        shadow = Creature(
            game,
            name='shadow',
            traits=Traits(hostile=False, mobile=True, evident=False),
            description=
            'You attempt to examine the shadow, but your vision blurs as you try to focus '
            'on its constantly shifting shape, preventing you from forming '
            'any clear impression',
            health=9001,
            strength=20,
            dexterity=90,
            location=east_tower)
        shadow.add_modifier('dark')
        shadow.entry_action = lambda g, p: Result(
            "A dark shadow enters.  The temperature in the room drops "
            "several degrees, as does your blood")
        shadow.exit_action = lambda g, p: Result(
            "The shadow leaves the room, and you once again find "
            "you can breathe freely")
        shadow.present_action = shadow_action

        # -------- Consequences: game milestones and achievements, and special results
        vocabulary = game.vocabulary

        open = vocabulary.lookup_verb_by_name('open')
        get = vocabulary.lookup_verb_by_name('get')
        ask = vocabulary.lookup_verb_by_name('ask')
        smell = vocabulary.lookup_verb_by_name('smell')
        jump = vocabulary.lookup_verb_by_name('jump')
        drop = vocabulary.lookup_verb_by_name('drop')
        throw = vocabulary.lookup_verb_by_name('throw')
        kill = vocabulary.lookup_verb_by_name('kill')
        unlock = vocabulary.lookup_verb_by_name('unlock')

        # Trying to pick up creatures turns them hostile
        for creature in vocabulary.get_objects_of_class(Creature):
            get.add_consequence(schema=Schema(roles={
                Role.AGENT: player,
                Role.THEME: creature
            }),
                                necessary_result=result.GENERIC_FAILURE,
                                effect=lambda schema: make_creature_hostile(
                                    schema[Role.THEME]))

        # Be careful in precarious places!
        for room in vocabulary.get_objects_of_class(Location):
            if room.traits.precarious:
                jump.add_consequence(
                    schema=Schema(roles={Role.AGENT: player}),
                    necessary_result=result.GENERIC_SUCCESS,
                    necessary_location=room,
                    effect=lambda schema: instant_death(player),
                    consequent_result=Success(
                        "In your excitement, you slip and fall to the hard ground far below!\n"
                        +
                        "You should probably be more careful where you do your jumping."
                    ))

                # TODO: Some kind of pattern-matching logic here.  This configures for _no_ theme, not _any_ theme ...
                throw.add_consequence(
                    schema=Schema(roles={Role.AGENT: player}),
                    necessary_result=result.GENERIC_FAILURE,
                    necessary_location=room,
                    effect=lambda schema: lose_item(player, schema[Role.THEME]
                                                    ),
                    consequent_result=Failure(
                        "You toss it carelessly, and it sails over the edge and out of sight"
                    ))

        # The mouse is too fast to catch or kill, but it's hungry
        def fed_mouse(crumbs):
            west_tower.remove_item(mouse)
            west_tower.remove_item(crumbs)
            west_tower.add_item(silver_key)

        get.add_consequence(
            schema=Schema(roles={
                Role.AGENT: player,
                Role.THEME: mouse
            }),
            necessary_result=result.GENERIC_SUCCESS,
            necessary_location=west_tower,
            effect=lambda schema: player.drop(mouse),
            consequent_result=Failure(
                "You try, but the mouse is far too small and fast for you to catch!"
            ))

        drop.add_consequence(schema=Schema(roles={
            Role.AGENT: player,
            Role.THEME: crumbs
        }),
                             necessary_result=result.GENERIC_SUCCESS,
                             necessary_location=west_tower,
                             effect=lambda schema: update_score(game, 20))

        drop.add_consequence(
            schema=Schema(roles={
                Role.AGENT: player,
                Role.THEME: crumbs
            }),
            necessary_result=result.GENERIC_SUCCESS,
            necessary_location=west_tower,
            effect=lambda schema: fed_mouse(crumbs),
            consequent_result=Success(
                "The mouse devours the crumbs, dropping something shiny to the floor in the "
                "process.  It then returns to its hole, well-fed and content"))

        # Foxes work cooperatively!
        def killed_fox(dead_fox, other_fox, key):
            dead_fox.remove_item(key)
            if other_fox.is_alive:
                other_fox.add_item(key)
                return result.GENERIC_SUCCESS
            else:
                return result.GENERIC_FAILURE

        kill.add_consequence(
            schema=Schema(roles={
                Role.AGENT: player,
                Role.THEME: red_fox
            }),
            necessary_result=result.GENERIC_SUCCESS,
            effect=killed_fox(red_fox, brown_fox, golden_key),
            consequent_result=Success(
                "As the red fox falls dead to the ground, its brother retrieves "
                "the golden key from its lifeless form"))

        kill.add_consequence(
            schema=Schema(roles={
                Role.AGENT: player,
                Role.THEME: brown_fox
            }),
            necessary_result=result.GENERIC_SUCCESS,
            effect=killed_fox(brown_fox, red_fox, bronze_key),
            consequent_result=Success(
                "As the brown fox falls dead to the ground, its brother retrieves "
                "the bronze key from its lifeless form"))

        # Achievement: unlock and open the sturdy door
        open.add_consequence(schema=Schema(roles={
            Role.AGENT: player,
            Role.PATIENT: sturdy_door
        }),
                             necessary_result=result.GENERIC_SUCCESS,
                             effect=lambda schema: update_score(game, 10))

        # Achievement: get the diamond from the villager
        ask.add_consequence(
            schema=Schema(roles={
                Role.AGENT: player,
                Role.PATIENT: villager,
                Role.THEME: diamond
            }),
            necessary_result=result.GENERIC_SUCCESS,
            effect=lambda schema: update_score(game, 20))

        get.add_consequence(schema=Schema(roles={
            Role.AGENT: player,
            Role.THEME: diamond
        }),
                            necessary_result=result.GENERIC_SUCCESS,
                            effect=lambda schema: update_score(game, 20))

        get.add_consequence(
            schema=Schema(roles={
                Role.AGENT: player,
                Role.PATIENT: villager,
                Role.THEME: diamond
            }),
            necessary_result=result.GENERIC_SUCCESS,
            effect=lambda schema: update_score(game, 20))

        # Health bonus: smell the sunflower
        smell.add_consequence(
            schema=Schema(roles={
                Role.AGENT: player,
                Role.THEME: flower
            }),
            necessary_result=result.GENERIC_SUCCESS,
            effect=lambda schema: update_health(player, 10),
            consequent_result=Success(
                "You feel revived by the bloom's invigorating fragrance"))

        def unlock_gate(this_lock, other_lock):
            this_lock.traits.locked = False
            if other_lock.traits.locked:
                return result.GENERIC_FAILURE
            else:
                update_score(game, 50)
                metal_gate.traits.closed = False
                return result.GENERIC_SUCCESS

        unlock.add_consequence(
            schema=Schema(
                roles={
                    Role.AGENT: player,
                    Role.THEME: golden_lock,
                    Role.INSTRUMENT: golden_key
                }),
            necessary_result=result.GENERIC_SUCCESS,
            effect=lambda schema: unlock_gate(golden_lock, bronze_lock),
            consequent_result=WON_GAME)

        unlock.add_consequence(
            schema=Schema(
                roles={
                    Role.AGENT: player,
                    Role.THEME: golden_lock,
                    Role.INSTRUMENT: golden_key
                }),
            necessary_result=result.GENERIC_SUCCESS,
            effect=lambda schema: unlock_gate(golden_lock, bronze_lock),
            consequent_result=WON_GAME)

        # Start game in crumbly room
        return crumbly_room
Beispiel #20
0
 def __init__(self, game, name, description=None, aliases=None):
     if description is None:
         description = 'An exit leading ' + name
     self.traits = Traits.merge(self, None, Direction.DEFAULT_TRAITS)
     super(Direction, self).__init__(game, name, description, aliases)
     self.add_goal_role('go')
Beispiel #21
0
class Creature(Item):

    DEFAULT_TRAITS = Traits(portable=False,
                            hostile=False,
                            friendly=False,
                            dead=False)

    def __init__(self,
                 game,
                 name,
                 description,
                 aliases=None,
                 traits=None,
                 health=0,
                 strength=0,
                 dexterity=0,
                 speed=50,
                 activity=33,
                 location=None,
                 entry_action=None,
                 present_action=None,
                 exit_action=None):
        self.traits = Traits.merge(self, traits, Creature.DEFAULT_TRAITS)
        super(Creature, self).__init__(game, name, description, aliases)
        self.health = health
        self.location = location
        self.strength = strength
        self.dexterity = dexterity
        self.last_movement = 0
        self.movement_frequency = int(100 / speed)
        self.movement_strategy = self.random_walk
        self.last_activity = 0
        self.activity_frequency = int(100 / activity)
        self.wanted_items = []
        if not entry_action:
            self.entry_action = self.default_entry
        if not exit_action:
            self.exit_action = self.default_exit
        if not present_action:
            self.present_action = self.neutral_action
        if self.location:
            self.location.add_item(self)
        self.inventory = Container(game,
                                   name + '-inv',
                                   name + "'s items",
                                   traits=Traits(closed=False),
                                   capacity=strength)
        self.add_patient_role('greet')
        self.add_patient_role('wave')
        self.add_patient_role('smile')
        self.add_patient_role('give')
        self.add_patient_role('ask')
        self.add_patient_role('kill')
        self.add_patient_role('take')

    def add_item(self, item):
        self.inventory.add_item(item)

    def remove_item(self, item):
        self.inventory.remove_item(item)

    def add_wanted_item(self, item):
        self.wanted_items.append(item)

    def become_friendly(self):
        self.traits.friendly = True
        self.present_action = self.friendly_action

    def become_hostile(self):
        self.traits.hostile = True
        self.present_action = self.hostile_action

    def default_entry(self, game, player):
        return Result("{} {} has entered the room".format(
            self.article().capitalize(), self.full_name()))

    def default_exit(self, game, player):
        return Result("The {} has exited the room".format(self.full_name()))

    def neutral_action(self, game, player):
        result = Result("The {} eyes you suspiciously".format(self.name))
        result.add_message(
            "The {} glances at you quickly, then away again".format(self.name))
        result.add_message("The {} coughs nervously".format(self.name))
        result.add_message(
            "The {} whistles tunelessly, avoiding your gaze".format(self.name))
        return result

    def friendly_action(self, game, player):
        result = Result(
            "The {} nods its head at you in friendly greeting".format(
                self.name))
        result.add_message("The {} smiles warmly at you".format(self.name))
        result.add_message("The {} leans against the wall of the room".format(
            self.name))
        result.add_message(
            "The {} hums a happy tune, hands in its pockets".format(self.name))
        return result

    def hostile_action(self, game, player):
        result = Result("The {} glares at you ferociously".format(self.name))
        result.add_message("The {} growls under its breath".format(self.name))
        result.add_message("The {} gnashes its teeth in anger".format(
            self.name))
        result.add_message("The {} clenches its fists with fury".format(
            self.name))
        return result

    def is_near_player(self):
        player = self.game.player
        return self.location == player.location

    def in_inventory(self, target):
        return target.id in self.inventory.items

    def is_alive(self):
        return self.health > 0

    def respond_to_greeting(self):
        if self.traits.hostile:
            result = Result("The {} snorts derisively at your greeting".format(
                self.name))
        elif self.traits.friendly:
            result = Result(
                "The {} gives you a friendly wave in return".format(self.name))
        else:
            result = Result(
                "The {} acknowledges your greeting with a curt nod".format(
                    self.name))
        return result

    def attack(self, game, player):
        r = Result("The {} attacks ... ".format(self.name))
        attack_difficulty = random.randint(0, 100)
        if self.dexterity < attack_difficulty:
            r.append("luckily, its blow fails to connect")
        else:
            r.append("and hits you!  You stagger back in pain ... ")
            player.health -= self.strength
        return r

    def should_interact(self, game, player):
        return self.last_activity == 0 \
               or self.activity_frequency <= game.turns - self.last_activity

    def act(self, game, player):
        self.last_activity = game.turns
        r = self.present_action(game, player)
        return r

    def should_move(self, game, player):
        return self.last_movement == 0 \
            or self.movement_frequency <= game.turns - self.last_movement

    def random_walk(self, game, location):
        passages = location.available_exits(game)
        if len(passages) > 0:
            p = random.choice(passages)
            p.go(game, self)
        return self.location

    def move(self, game, player):
        self.last_movement = game.turns
        start_location = self.location
        new_location = self.movement_strategy(game, start_location)
        if start_location != player.location \
                and new_location == player.location:
            r = self.entry_action(game, player)
        elif start_location == player.location \
                and new_location != player.location:
            r = self.exit_action(game, player)
        else:
            r = None
        return r

    def update(self, game, player):
        r = None
        if self.is_near_player():
            if self.traits.hostile:
                r = self.attack(game, player)
            elif self.should_interact(game, player):
                r = self.act(game, player)
            elif self.traits.mobile:
                r = self.move(game, player)
        elif self.traits.mobile:
            r = self.move(game, player)
        return r

    def describe(self):
        r = super(Creature, self).describe()
        item_count = len(self.inventory.items)
        if item_count == 1:
            item = self.vocabulary.lookup_noun(self.inventory.items[0])
            r.append(", carrying {} {}".format(item.article(), item.name))
        elif item_count > 1:
            r.append(", carrying: ")
            for item_id in self.inventory.items:
                item = self.vocabulary.lookup_noun(item_id)
                r.append("\n\t" + item.description)
        return r
Beispiel #22
0
class Container(Item):

    MAX_CAPACITY = 999
    DEFAULT_TRAITS = Traits(closed=True)

    def __init__(self,
                 game,
                 name,
                 description,
                 aliases=None,
                 traits=None,
                 size=0,
                 value=0,
                 capacity=0):
        self.traits = Traits.merge(self, traits, Container.DEFAULT_TRAITS)
        super(Container, self).__init__(game,
                                        name,
                                        description,
                                        aliases,
                                        size=size,
                                        value=value)
        self.vocab = game.vocabulary
        self.put_preposition = 'in'
        self.capacity = capacity
        self.items = []
        self.add_patient_role('put')
        self.add_patient_role('get')
        if not self.traits.surface:
            self.add_patient_role('open')
            self.add_patient_role('close')
            self.add_patient_role('lock')
            self.add_patient_role('unlock')

    def item_count(self):
        return len(self.items)

    def used_capacity(self):
        used_capacity = 0
        for item_id in self.items:
            item = self.vocab.lookup_noun(item_id)
            used_capacity += item.size
        return used_capacity

    def remaining_capacity(self):
        return self.capacity - self.used_capacity()

    def add_item(self, item, force=False):
        if not force and self.traits.closed:
            result = Failure("The " + self.full_name() + " is closed")
        else:
            if item.id in self.items:
                result = Failure("The {} {} already {} the {}".format(
                    item.full_name(), item.existential(), self.put_preposition,
                    self.full_name()))
            elif item.size > self.remaining_capacity():
                result = Failure("Sorry, the {} won't fit {} the {}".format(
                    item.full_name(), self.put_preposition, self.full_name()))
            else:
                self.items.append(item.id)
                result = Success("Okay, the {} {} now {} the {}".format(
                    item.full_name(), item.existential(), self.put_preposition,
                    self.full_name()))
        return result

    def remove_item(self, item, force=False):
        if not force and self.traits.closed:
            result = Failure("The " + self.full_name() + " is closed")
        else:
            if item.id in self.items:
                self.items.remove(item.id)
                result = Success("You remove the " + item.name + " from the " +
                                 self.full_name())
            else:
                result = Failure("The {} {}n't {} the {}!".format(
                    item.full_name(), item.existential(), self.put_preposition,
                    self.full_name()))
        return result

    def append_container_description(self, description):
        item_count = len(self.items)
        if item_count == 0:
            description.append(", containing nothing")
        elif len(self.items) == 1:
            item = self.vocab.lookup_noun(self.items[0])
            description.append(", containing {} {}".format(
                item.article(), item.name))
        else:
            description.append(", containing: ")
            for item_id in self.items:
                item = self.vocab.lookup_noun(item_id)
                description.append("\n\t" + item.article() + " " +
                                   item.full_name())

    def describe(self):
        r = super(Container, self).describe()
        if self.traits.closed:
            r.append(", which is currently closed")
        else:
            self.append_container_description(r)
        return r