Ejemplo n.º 1
0
def read(book):
    book_name = book
    _, book = _find_available_item(book_name)
    if book is None:
        say.insayne(f"There is no {book_name} here to read.")
    else:
        book.read(G.player)
Ejemplo n.º 2
0
 def librarian_death_throes(librarian):
     say.insayne(
         "The librarian grins impossibly wide. A thin rivulet of blood "
         "appears between his teeth. His eyes roll back and, with a giggle, "
         "he falls backward onto the ground as though reclining on a divan."
     )
     say.insayne("The edge of a hidebound book peeks from his rags.")
Ejemplo n.º 3
0
def ability(ability):
    ability_name = ability
    the_ability = G.player.abilities.get(ability_name)
    if the_ability is None:
        say.insayne("You know no such ability.")
    else:
        the_ability.activate()
Ejemplo n.º 4
0
def _resolve_attack(attacker, attack):
    # TODO: Add equipment, different damage dice, etc.
    # TODO: Respect attack.method.
    defender = attack.target
    is_player = attacker is G.player
    if is_player:
        subj, obj = ["you", defender.name]
    else:
        subj, obj = [attacker.name, "you"]
    subj = util.capitalized(subj)
    miss = "miss" if is_player else "misses"
    hit = "hit" if is_player else "hits"

    strength_mod = int((attacker.strength.value - 10) / 2)
    to_hit = strength_mod + dice.roll("1d20")
    if to_hit < (10 + (defender.stamina.value - 10) / 2):
        say.insayne(f"{subj} {miss}.")

    else:
        damage = dice.roll("1d8") + strength_mod
        # TODO: How to organize messages better? Death also creates text, so
        # there should be a way to make sure the messages are ordered.
        say.insayne(f"{subj} {hit} {obj} for {damage} damage!")
        # TODO: Attack should have associated text which is consulted here.
        defender.health.heal_or_harm(
            -1 * damage, cause=f"the fins of {util.a(attacker.name)}")

    if not (is_player or defender.alive):
        G.just_died = True
Ejemplo n.º 5
0
def drop(item):
    item_name = item
    item = G.player.inventory.find(item_name)
    if item is None:
        say.insayne(f"You don't have {util.a(item_name)} to drop.")
    else:
        say.insayne(f"You drop the {item_name} on the ground.")
        _move_item(G.player.inventory, G.player.current_room.items, item)
Ejemplo n.º 6
0
 def activate(self):
     # TODO: Add global invocation counter.
     # TODO: Make events global and event polling constant so that this
     # ability can win the game.
     _say.insayne("You touch the ground. You sit and breathe, are still.")
     _say.insayne("The stillness soothes your mind and steels your will.",
                  add_newline=False)
     self.owner.insanity.modify(-20)
Ejemplo n.º 7
0
 def execute(self):
     # TODO: This kind of thing could be handled with a description generator.
     say.insayne(
         "You are in a dark tube. The walls and floor quiver at your touch, "
         "and you realize this is the intestine of a vast behemoth.")
     G.player.insanity.modify(10)
     # TODO: change to self.room.description
     G.player.current_room.description = (
         "The walls and floor of the intestine room shudder at your step.")
     self._will_execute = False
Ejemplo n.º 8
0
def use(item, verb):
    item_name = item
    item = G.player.inventory.find(item_name)
    if item is None:
        say.insayne(f"You don't have {util.a(item_name)}.")
        return
    try:
        item.consume(G.player)
    except AttributeError:
        say.insayne(f"You can't {verb} the {item_name}.")
Ejemplo n.º 9
0
    def read(self, actor):
        if actor.has_read(self):
            # TODO: Condition pronoun/verb agreement on whether actor is
            # player or not. Make a utility function for this.
            say.insayne(
                "You have already tasted the wisdom distilled in this book.")

        else:
            actor.add_ability(ability.meditation())
            actor.set_has_read(self)
Ejemplo n.º 10
0
def go(direction):
    direction = direction.lower()
    next_room, the_direction = G.player.current_room.exit(direction)
    if next_room is None:
        say.insayne(f"It is not possible to proceed {direction}.")
    else:
        # TODO: Replace enqueue_text with text events.
        say.insayne(f"You proceed {the_direction.display_description}.")
        G.player.current_room.on_exit()
        G.player.current_room = next_room
        enter_room(G.player.current_room)
Ejemplo n.º 11
0
def _look():
    if G.player.current_room.description:
        say.insayne(G.player.current_room.description)

    # TODO: Bespoke descriptions for all items and characters.
    for item in G.player.current_room.items:
        say.insayne(item.idle_description)
    for character in G.player.current_room.npcs:
        say.insayne(character.idle_text)
    for corpse in G.player.current_room.corpses:
        say.insayne(f"The corpse of {util.a(corpse.name)} molders here.")
    say.insayne(f'Exits are {", ".join(G.player.current_room.display_exits)}.')
Ejemplo n.º 12
0
 def die(self, cause=None):
     if self is G.player:
         cause = cause or self.health.last_cause
         G.cause_of_death = cause
         say.insayne("You die.")
         say.insayne("...")
         raise tartarus.RaptureException("")
     else:
         self.alive = False
         self._death_throes(self)
         self.current_room.characters.remove(self)
         self.current_room.corpses.add(self)
Ejemplo n.º 13
0
 def execute(self):
     self._counter += 1
     if self.room is not G.player.current_room:
         return
     if self._counter % self._TURNS_TO_CHIME == 0:
         say.insayne(
             "Suddenly, the bells begin to chime in simultaneity, if not "
             "exactly unison. From the chaos can be discerned a discordant "
             "melody. It is a blasphemous all-bells rendition of Hanson's "
             "'MMMBop.' As the final incomprehensible peal subsides, you "
             "realize the song is stuck in your head, threatening the "
             "sinews of your very sanity.")
         G.player.insanity.modify(15)
         G.add_event(_SongInHeadEvent(), "pre")
Ejemplo n.º 14
0
 def die(self, cause=None):
     cause = cause or self.health.last_cause
     self.alive = False
     is_player = self is G.player
     if is_player:
         G.player.current_room.on_exit()
         G.cause_of_death = cause
         say.insayne("You die.")
         say.insayne("...")
     self._death_throes(self)
     if is_player:
         return
     self.current_room.characters.remove(self)
     self.current_room.corpses.add(self)
Ejemplo n.º 15
0
def _get_random_start():
    # TODO: Condition this on how the last death actually occurred.
    death_text = _G.cause_of_death or random.choice([
        "being impaled",
        "slowly suffocating as a glabrous tentacle horror looks on",
    ])
    for text in [
            f"You recall your death by {death_text}. The memory fades away.",
            "You know only that you have been here for interminable years, "
            "that you have died innumerable times, and that someone once told "
            "you there was a way out. You were told this an eon ago, or maybe "
            "a day, but the stubborn hope of escape glisters in your mind.",
    ]:
        say.insayne(text)
Ejemplo n.º 16
0
def stats():
    lines = []
    name_str = f"{G.player.name}"
    lines.append(name_str)
    # TODO: Unfuck this for zalgo.
    lines.append("".join("-" for _ in name_str))
    for stat in G.player.all_stats():
        if hasattr(stat, "current_value"):
            lines.append(f"{stat.name:10}: {stat.current_value}/{stat.value}")
        else:
            lines.append(f"{stat.name:10}: {stat.value}")
    lines.append("".join("-" for _ in name_str))
    lines.append("- abilities -")
    for ability_name, ability in sorted(G.player.abilities.items()):
        if ability_name:
            lines.append(f"{ability_name}: {ability.DESCRIPTION}")

    for i, line in enumerate(lines):
        add_newline = i == 0
        say.insayne(line, add_newline=add_newline)
Ejemplo n.º 17
0
    def consume(self, consumer):
        if not consumer.inventory.find('lighter'):
            if consumer is _G.player:
                say.insayne(f'You have no way to light the {self.name}.')
                return
        consumer.inventory.remove(self)
        cigarette_butt = CigaretteButt.create()
        consumer.inventory.add(cigarette_butt)
        consumer.current_room.items.add(Smoke.create(consumer.current_room))

        if consumer is _G.player:
            say.insayne(
                f"You take a furtive puff on the {self.name}. It tastes foul "
                "and acrid. You do not feel like you are wearing a leather "
                "jacket at all.")
            consumer.psyche.heal_or_harm(dice.roll("1d2"))
            # TODO: Make insanity a variable statistic?
            consumer.insanity.modify(-dice.roll("1d2"))
            # TODO: Interesting problem with how this is implemented:
            # because text is not queued but printed directly, if this line
            # precedes anything else in this function and player dies,
            # weird stuff will ensue.
            consumer.health.heal_or_harm(-dice.roll("2d2"),
                                         cause="smoking half a cig")
        else:
            name = util.capitalized(consumer.name)
            say.insayne(f"{name} puffs furtively on a {self.name}.")
Ejemplo n.º 18
0
    def consume(self, consumer):
        if not consumer.inventory.find('lighter'):
            if consumer is _G.player:
                say.insayne(f'You have no way to light the {self.name}.')
                return

        # TODO: Buff strength for a little bit.
        # TODO: Heal insanity, restore psyche.
        # TODO: I don't like this solution as it presumes the item is in the
        # consumer's inventory. Maybe that is a fine assumption. If not,
        # consider storing the inventory relationship as two-way.
        consumer.inventory.remove(self)
        cigarette_stub = CigaretteStub.create()
        consumer.inventory.add(cigarette_stub)
        consumer.current_room.items.add(Smoke.create(consumer.current_room))

        # TODO: Customize text based on whether consumer is player.
        # TODO: Add location to actors so that the state of onlookers can
        # be properly assessed.
        aliases = random.sample(self.aliases, 2)
        if consumer is _G.player:
            say.insayne(
                f"You take a long, smooth drag on the {aliases[0]}. Time seems "
                "to mellow; all activity nearby slows. Onlookers watch as you "
                "draw measured, pensive little puffs from the delicious "
                f"{aliases[1]}. You look very cool.")
            consumer.health.heal_or_harm(-dice.roll("1d2"), cause="being cool")
            consumer.psyche.heal_or_harm(dice.roll("2d2"))
            # TODO: Make insanity a variable statistic?
            consumer.insanity.modify(-dice.roll("2d2"))
        else:
            name = util.capitalized(consumer.name)
            say.insayne(
                f"{name} puffs mellowly on a {self.name}, looking extremely fly."
            )
Ejemplo n.º 19
0
def take(item):
    item_name = item
    location, item = _find_in_room(item_name)
    if location is None or item is None:
        if G.player.current_room.npcs.find(item_name):
            say.insayne("You cannot take sentient beings.")
        elif G.player.current_room.corpses.find(item_name):
            say.insayne("The corpse would be too burdensome to carry.")
        else:
            say.insayne(f"There is no {item_name} here to take.")
    elif not item.obtainable:
        say.insayne("You can't take the {item_name}.")
    else:
        _move_item(location, G.player.inventory, item)
Ejemplo n.º 20
0
def inventory():
    say.insayne("You possess the following:")
    if not G.player.inventory:
        say.insayne("Nothing.", add_newline=False)
        return
    # TODO: This is repeated in inspect(). Collapse these into a single function.
    for item in G.player.inventory:
        say.insayne(item.description, add_newline=False)
Ejemplo n.º 21
0
        def execute(self):
            # TODO: Should not be G.player--what if somebody else wants a smoke?
            if _G.player.inventory.find(
                    "cigarette") or _G.player.inventory.find("stub"):
                say.insayne(
                    'The smoker clucks his tongue. "You\'ve already got a '
                    'smoke; why are you trying to bum one off me?"')

            else:
                cigarette = random.choice(
                    [items.Cigarette, items.CigaretteStub]).create()
                say.insayne(
                    '"Here you go," says the smoker between puffs. "Have a '
                    'smoke with me. It\'s all there is to do here, man. Just '
                    'that and wait to die and live again."')
                _G.player.inventory.add(cigarette)
                lighter = npc.inventory.find("lighter")
                if lighter is not None:
                    say.insayne('"Here, you\'ll need this, too."')
                    npc.inventory.remove(lighter)
                    _G.player.inventory.add(lighter)
                    say.insayne('"No smoke without fire."')
Ejemplo n.º 22
0
def loot(item, corpse):
    item_name = item
    corpse_name = corpse
    character = _get_present_actor(corpse_name)

    if character is None:
        say.insayne(f"There is no {corpse_name} here.")
        return

    # TODO: Really need some abstraction around combat turns to avoid this
    # duplication.
    if character.alive:
        message = "You cannot loot the living!"
        # TODO: What if none of the character present chooses to attack?
        if G.player.current_room.npcs:
            message += " All enemies attack as your clumsy pickpocketing attempt fails."
        say.insayne(message)
        for character in G.player.current_room.npcs:
            if G.just_died:
                return
            assert character.ai is not None
            action = character.ai.choose_action(G.player.current_room)
            if action.attack is not None:
                _resolve_attack(character, action.attack)
        return

    else:
        if item_name in {"all", "everything"}:
            items = {item.name: item for item in character.inventory}
        else:
            items = {item_name: character.inventory.find(item_name)}
        for name, item in items.items():
            if item is None:
                say.insayne(f"There is no {name} on the corpse.")
            else:
                say.insayne(f"You liberate {item.name} from the corpse.")
                _move_item(character.inventory, G.player.inventory, item)
Ejemplo n.º 23
0
def attack(actor):
    """Attacks another character in the same room."""
    actor_name = actor  # Variable names are constrained by adventurelib.
    # TODO: Consider turn order--some kind of agility stat?
    # TODO: Other actions should be considered "combat" actions. Implement some
    # notion of turns.
    # TODO: Other combat actions--spells, items, fleeing, etc.
    # TODO: Affinity/factions so monsters can choose whom to strike.
    defender = _get_present_actor(actor_name)
    if defender is None:
        say.insayne(f"There is no {actor_name} here to attack.")
        return

    if not defender.alive:
        say.insayne(
            f"In a blind fury, you hack uselessly at the {defender.name}'s corpse."
        )
        G.player.insanity.modify(10)
        return

    # TODO: Move this to player AI. Use defender as a "hint."
    # TODO: Migrating to player AI will avoid the special-casing of Room.npcs.
    # TODO: Migrating to player AI can also allow for a more nuanced
    # menu when attacking.
    _resolve_attack(G.player, ai.Attack(target=defender, method=None))
    for character in G.player.current_room.npcs:
        if G.just_died:
            return
        assert character.ai is not None
        action = character.ai.choose_action(G.player.current_room,
                                            impulse="attack")
        if action.attack is not None:
            _resolve_attack(character, action.attack)
        else:
            say.insayne(
                f"{util.capitalized(character.name)} makes no hostile motion.")
Ejemplo n.º 24
0
 def execute(self):
     say.insayne(
         "Tittering and in a halted voice, the librarian utters these words:"
     )
     say.insayne("\"The flesh is active, yet it's only soothed")
     say.insayne("when still. Through constant paradox, flesh moves.",
                 add_newline=False)
     say.insayne("I hold stillness's secret! It is mine!",
                 add_newline=False)
     say.insayne("Find it upon my body when I die.\"",
                 add_newline=False)
     say.insayne(
         "\"Now watch,\" cries the rag-clad wretch, \"as the paradox "
         "is resolved.\"")
     npc.die()
     self._will_execute = False
Ejemplo n.º 25
0
 def _discard_aliases(self, item):
     super()._discard_aliases(item)
     item.holder = None
     if self is _G.player.inventory:
         say.insayne(f"You lose possession of {item.name}.")
Ejemplo n.º 26
0
 def _add_aliases(self, item):
     super()._add_aliases(item)
     logging.debug(f'_add_aliases called with {item}')
     item.holder = self
     if self is _G.player.inventory:
         say.insayne(f"You acquire {item.name}.")
Ejemplo n.º 27
0
 def consume(self, consumer):
     # TODO: Customize text based on whether consumer is player.
     say.insayne(f"You eat the {self.name}. What is wrong with you?")
     consumer.insanity.modify(10)
     consumer.inventory.remove(self)
Ejemplo n.º 28
0
 def smoker_death_throes(smoker):
     say.insayne(
         'The smoker glances placidly around the environs. "Until the next '
         'time around, I guess." With a final nod, he breathes a perfect '
         'wreath of smoke, which dissipates solemnly.')
Ejemplo n.º 29
0
 def fish_man_death_throes(fish_man):
     say.insayne(
         f"{util.capitalized(fish_man.name)} flops breathlessly upon the "
         "ground, blood commingling with piscine slobber. Half-formed gills "
         "flutter helplessly, urgently, then fall slack.")
Ejemplo n.º 30
0
 def add_ability(self, ability):
     # TODO: "You" vs. name problem as always.
     say.insayne(f"You gain the ability {ability.NAME}.")
     # TODO: Bidirectional reference problem as always.
     ability.owner = self
     self._abilities[ability.NAME] = ability