Beispiel #1
0
    def __init__(
        self, ctx: Context, attack: int, defense: int, width: int = 15, height: int = 15
    ) -> None:
        self.ctx = ctx

        self.original_hp = attack * 100
        self.original_enemy_hp = attack * 10

        self.width = width
        self.height = height
        self.maze = Maze.generate(width=width, height=height)
        self.player_x = 0
        self.player_y = 0
        self.attack = attack
        self.defense = defense
        self.hp = attack * 100

        self.heal_hp = round(attack * 0.25) or 1
        self.min_dmg = round(attack * 0.5)
        self.max_dmg = round(attack * 1.5)

        self.enemy_hp: int | None = None

        self.message: discord.Message | None = None
        self.status_text: str | None = _("The active adventure has started.")
Beispiel #2
0
    async def activeadventure(self, ctx):
        _(
            # xgettext: no-python-format
            """Active adventures will put you into a 15x15 randomly generated maze. You will begin in the top left corner (0,0) and your goal is to find the exit in the bottom right corner (14,14)
            You control your character with the arrow reactions below the message.

            You have 1000HP. The adventure ends when you find the exit or your HP drop to zero.
            You can lose HP by getting damaged by traps or enemies.

            The maze contains safe spaces and treasures but also traps and enemies.
            Each space has a 10% chance of being a trap. If a space does not have a trap, it has a 10% chance of having an enemy.
            Each maze has 5 treasure chests.

            Traps can damage you from 30 to 120 HP.
            Enemy damage is based on your own damage. During enemy fights, you can attack (⚔️), defend (🛡️) or recover HP (❤️)
            Treasure chests can have gold up to 25 times your attack + defense.

            If you reach the end, you will receive a special treasure with gold up to 100 times your attack + defense.

            (It is recommended to draw a map of the maze)
            (This command has a cooldown of 30 minutes)""")
        if not await ctx.confirm(
                _("You are going to be in a labyrinth of size 15x15. There are enemies,"
                  " treasures and hidden traps. Reach the exit in the bottom right corner"
                  " for a huge extra bonus!\nAre you ready?\n\nTip: Use a silent channel"
                  " for this, you may want to read all the messages I will send."
                  )):
            return

        msg = await ctx.send(_("**Generating a maze...**"))

        maze = Maze.generate(15, 15)
        direction_emojis = {
            "n": "\U00002b06",
            "e": "\U000027a1",
            "s": "\U00002b07",
            "w": "\U00002b05",
        }
        direction_emojis_inverse = {
            val: key
            for key, val in direction_emojis.items()
        }
        direction_names = {
            "n": _("North"),
            "e": _("East"),
            "s": _("South"),
            "w": _("West"),
        }
        all_directions = set(direction_names.keys())
        x = 0
        y = 0

        attack, defense = await self.bot.get_damage_armor_for(ctx.author)

        attack = int(attack)
        defense = int(defense)

        hp = 1000

        def free(cell):
            return all_directions - cell.walls

        def fmt_direction(direction):
            return direction_names[direction]

        def player_pos():
            return maze[x, y]

        def is_at_end():
            return x == 14 and y == 14

        def move(x, y, direction):
            if direction == "n":
                y = y - 1
            elif direction == "e":
                x = x + 1
            elif direction == "s":
                y = y + 1
            elif direction == "w":
                x = x - 1
            return x, y

        async def wait_for_move():
            possible = free(player_pos())
            needed = [direction_emojis[direction] for direction in possible]
            try:
                await msg.clear_reactions()
            except discord.Forbidden:
                for r in msg.reactions:
                    if str(r.emoji) not in needed:
                        await msg.remove_reaction(r, ctx.guild.me)
                for r in needed:
                    if r not in [str(r.emoji) for r in msg.reactions]:
                        await msg.add_reaction(r)
            else:
                for direction in possible:
                    await msg.add_reaction(direction_emojis[direction])

            def check(r, u):
                return (u == ctx.author and r.message.id == msg.id
                        and direction_emojis_inverse.get(str(r.emoji),
                                                         None) in possible)

            r, u = await self.bot.wait_for("reaction_add",
                                           check=check,
                                           timeout=30)

            return direction_emojis_inverse[str(r.emoji)]

        async def update():
            text = ""
            pos = player_pos()
            for direction in ("n", "e", "s", "w"):
                side = fmt_direction(direction)
                fake_x, fake_y = move(x, y, direction)
                fake_cell = maze[fake_x, fake_y]
                if direction in pos.walls:
                    text2 = _("To the {side} is a wall.").format(side=side)
                elif fake_cell.enemy:
                    text2 = _("To the {side} is an enemy.").format(side=side)
                elif fake_cell.treasure:
                    text2 = _("To the {side} is a treasure.").format(side=side)
                else:
                    text2 = _("To the {side} is a floor.").format(side=side)
                text = f"{text}\n{text2}"

            text2 = _("You are on {hp} HP").format(hp=hp)
            text = f"{text}\n\n{text2}"

            await msg.edit(content=text)

        async def handle_specials(hp):
            cell = player_pos()
            if cell.trap:
                damage = random.randint(30, 120)
                await ctx.send(
                    _("You stepped on a trap and took {damage} damage!").
                    format(damage=damage))
                cell.trap = False  # Remove the trap
                return hp - damage
            elif cell.treasure:
                val = attack + defense
                money = random.randint(val, val * 25)
                await self.bot.pool.execute(
                    'UPDATE profile SET "money"="money"+$1 WHERE "user"=$2;',
                    money,
                    ctx.author.id,
                )
                await self.bot.log_transaction(
                    ctx,
                    from_=1,
                    to=ctx.author.id,
                    subject="money",
                    data={"Amount": money},
                )
                await ctx.send(
                    _("You found a treasure with **${money}** inside!").format(
                        money=money))
                cell.treasure = False
            elif cell.enemy:

                def to_bar(hp):
                    fields = hp // 100
                    return f"[{'▯' * fields}{'▮' * (10 - fields)}]"

                def is_valid_move(r, u):
                    return (r.message.id == msg.id and u == ctx.author
                            and str(r.emoji) in emojis)

                emojis = {
                    "\U00002694": "attack",
                    "\U0001f6e1": "defend",
                    "\U00002764": "recover",
                }
                enemy = _("Enemy")
                enemy_hp = 1000
                heal_hp = round(attack * 0.25) or 1
                min_dmg = round(attack * 0.5)
                max_dmg = round(attack * 1.5)
                status1 = _("The Fight started")
                status2 = ""

                await msg.clear_reactions()
                for emoji in emojis:
                    await msg.add_reaction(emoji)

                while enemy_hp > 0 and hp > 0:
                    await msg.edit(content=f"""\
```
{ctx.author.name}{" " * (38 - len(ctx.author.name) - len(enemy))}{enemy}
------------++++++++++++++------------
{to_bar(hp)}  {hp}  {enemy_hp}    {to_bar(enemy_hp)}

{status1}
{status2}
```""")

                    r, u = await self.bot.wait_for("reaction_add",
                                                   check=is_valid_move,
                                                   timeout=30)

                    try:
                        await msg.remove_reaction(r, u)
                    except discord.Forbidden:
                        pass

                    enemy_move = random.choice(["attack", "defend", "recover"])
                    player_move = emojis[str(r.emoji)]

                    if enemy_move == "recover":
                        enemy_hp += heal_hp
                        enemy_hp = 1000 if enemy_hp > 1000 else enemy_hp
                        status1 = _("The Enemy healed themselves for {hp} HP"
                                    ).format(hp=heal_hp)
                    if player_move == "recover":
                        hp += heal_hp
                        hp = 1000 if hp > 1000 else hp
                        status2 = _("You healed yourself for {hp} HP").format(
                            hp=heal_hp)
                    if (enemy_move == "attack" and player_move
                            == "defend") or (enemy_move == "defend"
                                             and player_move == "attack"):
                        status1 = _("Attack blocked.")
                        status2 = ""
                    if enemy_move == "attack" and player_move != "defend":
                        eff = random.randint(min_dmg, max_dmg)
                        hp -= eff
                        status1 = _("The Enemy hit you for {dmg} damage"
                                    ).format(dmg=eff)
                    if player_move == "attack" and enemy_move != "defend":
                        enemy_hp -= attack
                        status2 = _("You hit the enemy for {dmg} damage"
                                    ).format(dmg=attack)

                if enemy_hp <= 0:
                    cell.enemy = False

            return hp

        while not is_at_end():
            await update()
            try:
                direction = await wait_for_move()
            except asyncio.TimeoutError:
                await self.bot.reset_cooldown(ctx)
                return await msg.edit(content=_("Timed out."))
            x, y = move(x, y,
                        direction)  # Python namespacing sucks, to be honest
            try:
                hp = await handle_specials(hp
                                           )  # Should've used a class for this
            except asyncio.TimeoutError:
                await self.bot.reset_cooldown(ctx)
                return await msg.edit(content=_("Timed out."))
            if hp <= 0:
                return await ctx.send(_("You died."))

        val = attack + defense
        money = random.randint(val * 5, val * 100)
        await self.bot.pool.execute(
            'UPDATE profile SET "money"="money"+$1 WHERE "user"=$2;',
            money,
            ctx.author.id,
        )
        await self.bot.log_transaction(ctx,
                                       from_=1,
                                       to=ctx.author.id,
                                       subject="money",
                                       data={"Amount": money})

        await ctx.send(
            _("You have reached the exit and were rewarded **${money}** for getting"
              " out!").format(money=money))
Beispiel #3
0
    async def activeadventure(self, ctx):
        _("""Go out on a docile adventure controlled by reactions.""")
        if not await ctx.confirm(
                _("You are going to be in a labyrinth of size 15x15. There are enemies, treasures and hidden traps. Reach the exit in the bottom right corner for a huge extra bonus!\nAre you ready?\n\nTip: Use a silent channel for this, you may want to read all the messages I will send."
                  )):
            return

        msg = await ctx.send(_("**Generating a maze...**"))

        maze = Maze.generate(15, 15)
        direction_emojis = {
            "n": "\U00002b06",
            "e": "\U000027a1",
            "s": "\U00002b07",
            "w": "\U00002b05",
        }
        direction_emojis_inverse = {
            val: key
            for key, val in direction_emojis.items()
        }
        direction_names = {
            "n": _("North"),
            "e": _("East"),
            "s": _("South"),
            "w": _("West"),
        }
        all_directions = set(direction_names.keys())
        x = 0
        y = 0

        sword, shield = await self.bot.get_equipped_items_for(ctx.author)
        attack, defense = await self.bot.generate_stats(
            ctx.author,
            float(sword["damage"] if sword else 0),
            float(shield["armor"] if shield else 0),
        )

        attack = int(attack)
        defense = int(defense)

        hp = 1000

        def free(cell):
            return all_directions - cell.walls

        def fmt_direction(direction):
            return direction_names[direction]

        def player_pos():
            return maze[x, y]

        def is_at_end():
            return x == 14 and y == 14

        def move(x, y, direction):
            if direction == "n":
                y = y - 1
            elif direction == "e":
                x = x + 1
            elif direction == "s":
                y = y + 1
            elif direction == "w":
                x = x - 1
            return x, y

        async def wait_for_move():
            possible = free(player_pos())
            needed = [direction_emojis[direction] for direction in possible]
            try:
                await msg.clear_reactions()
            except discord.Forbidden:
                for r in msg.reactions:
                    if str(r.emoji) not in needed:
                        await msg.remove_reaction(r, ctx.guild.me)
                for r in needed:
                    if r not in [str(r.emoji) for r in msg.reactions]:
                        await msg.add_reaction(r)
            else:
                for direction in possible:
                    await msg.add_reaction(direction_emojis[direction])

            def check(r, u):
                return (u == ctx.author and r.message.id == msg.id
                        and direction_emojis_inverse.get(str(r.emoji),
                                                         None) in possible)

            r, u = await self.bot.wait_for("reaction_add",
                                           check=check,
                                           timeout=30)

            return direction_emojis_inverse[str(r.emoji)]

        async def update():
            text = ""
            pos = player_pos()
            for direction in ("n", "e", "s", "w"):
                side = fmt_direction(direction)
                fake_x, fake_y = move(x, y, direction)
                fake_cell = maze[fake_x, fake_y]
                if direction in pos.walls:
                    text2 = _("To the {side} is a wall.").format(side=side)
                elif fake_cell.enemy:
                    text2 = _("To the {side} is an enemy.").format(side=side)
                elif fake_cell.treasure:
                    text2 = _("To the {side} is a treasure.").format(side=side)
                else:
                    text2 = _("To the {side} is a floor.").format(side=side)
                text = f"{text}\n{text2}"

            text2 = _("You are on {hp} HP").format(hp=hp)
            text = f"{text}\n\n{text2}"

            await msg.edit(content=text)

        async def handle_specials(hp):
            cell = player_pos()
            if cell.trap:
                damage = random.randint(30, 120)
                await ctx.send(
                    _("You stepped on a trap and took {damage} damage!").
                    format(damage=damage))
                cell.trap = False  # Remove the trap
                return hp - damage
            elif cell.treasure:
                val = attack + defense
                money = random.randint(val, val * 25)
                await self.bot.pool.execute(
                    'UPDATE profile SET "money"="money"+$1 WHERE "user"=$2;',
                    money,
                    ctx.author.id,
                )
                await ctx.send(
                    _("You found a treasure with **${money}** inside!").format(
                        money=money))
                cell.treasure = False
            elif cell.enemy:

                def to_bar(hp):
                    fields = hp // 100
                    return f"[{'▯' * fields}{'▮' * (10 - fields)}]"

                def is_valid_move(r, u):
                    return (r.message.id == msg.id and u == ctx.author
                            and str(r.emoji) in emojis)

                emojis = {
                    "\U00002694": "attack",
                    "\U0001f6e1": "defend",
                    "\U00002764": "recover",
                }
                enemy = _("Enemy")
                enemy_hp = 1000
                heal_hp = round(attack * 0.25) or 1
                min_dmg = round(attack * 0.5)
                max_dmg = round(attack * 1.5)
                status1 = _("The Fight started")
                status2 = ""

                await msg.clear_reactions()
                for emoji in emojis:
                    await msg.add_reaction(emoji)

                while enemy_hp > 0 and hp > 0:
                    await msg.edit(content=f"""\
```
{ctx.author.name}{" " * (38 - len(ctx.author.name) - len(enemy))}{enemy}
------------++++++++++++++------------
{to_bar(hp)}  {hp}  {enemy_hp}    {to_bar(enemy_hp)}

{status1}
{status2}
```""")

                    r, u = await self.bot.wait_for("reaction_add",
                                                   check=is_valid_move,
                                                   timeout=30)

                    try:
                        await msg.remove_reaction(r, u)
                    except discord.Forbidden:
                        pass

                    enemy_move = random.choice(["attack", "defend", "recover"])
                    player_move = emojis[str(r.emoji)]

                    if enemy_move == "recover":
                        enemy_hp += heal_hp
                        enemy_hp = 1000 if enemy_hp > 1000 else enemy_hp
                        status1 = _("The Enemy healed themselves for {hp} HP"
                                    ).format(hp=heal_hp)
                    if player_move == "recover":
                        hp += heal_hp
                        hp = 1000 if hp > 1000 else hp
                        status2 = _("You healed yourself for {hp} HP").format(
                            hp=heal_hp)
                    if (enemy_move == "attack" and player_move
                            == "defend") or (enemy_move == "defend"
                                             and player_move == "attack"):
                        status1 = _("Attack blocked.")
                        status2 = ""
                    if enemy_move == "attack" and player_move != "defend":
                        eff = random.randint(min_dmg, max_dmg)
                        hp -= eff
                        status1 = _("The Enemy hit you for {dmg} damage"
                                    ).format(dmg=eff)
                    if player_move == "attack" and enemy_move != "defend":
                        enemy_hp -= attack
                        status2 = _("You hit the enemy for {dmg} damage"
                                    ).format(dmg=attack)

                if enemy_hp <= 0:
                    cell.enemy = False

            return hp

        while not is_at_end():
            await update()
            try:
                direction = await wait_for_move()
            except asyncio.TimeoutError:
                return await msg.edit(content=_("Timed out."))
            x, y = move(x, y,
                        direction)  # Python namespacing sucks, to be honest
            try:
                hp = await handle_specials(hp
                                           )  # Should've used a class for this
            except asyncio.TimeoutError:
                return await msg.edit(content=_("Timed out."))
            if hp <= 0:
                return await ctx.send(_("You died."))

        val = attack + defense
        money = random.randint(val * 5, val * 100)
        await self.bot.pool.execute(
            'UPDATE profile SET "money"="money"+$1 WHERE "user"=$2;',
            money,
            ctx.author.id,
        )

        await ctx.send(
            _("You have reached the exit and were rewarded **${money}** for getting out!"
              ).format(money=money))
Beispiel #4
0
 async def run(self):
     maze = Maze.generate(50, 50)
     for player in self.players:
         await self.bot.loop.create_task(player.run(maze, self.bot))