Untitled

 avatar
Melon
python
3 months ago
12 kB
7
Indexable
ATTEMPT_VALUES  = [1, 2, 3, 4, 5, 6]
ATTEMPT_WEIGHTS = [2, 12, 15, 12, 8, 7]

MAX_PASSES = 10
MAX_BALLS  = 10

FAIL_TEXT = [
    "Le Pokémon se débat !",
    "La Pokéball a rebondit !",
    "Il esquive la balle !",
    "La Pokéball s'ouvre !",
    "Ça y était presque !",
    "Il veut te résister !",
    "La cible a esquivé !"
]

UI = {
    "caught":   "<:caught:1379044694922760282>",
    "uncaught": "<:uncaught:1379044710437617674>",
    "waiting":  "<a:waiting:1379500515343470613>",
    "success":  "<a:success:1379500529171955722>",
    "random":   "<a:random:1379500466945130597>",
    "fled":     "<a:fled:1379500541037514833>",
    "shiny":    "<a:shiny:1377962995552096388>"
}


db = lambda c: SlashMongo("inventory", c)
items   = db("items")
daycare = db("daycare")
pokedex = db("emojis")
daily   = db("daily")
icons   = db("ressource")


# -------------------- VIEW --------------------

class PokeCatch(View):

    def __init__(self, user_id):
        self.user_id = user_id
        self.query = {"user_id": str(user_id)}

        self.passes = MAX_PASSES
        self.balls = MAX_BALLS

        self.tries = 0
        self.captures = 0

        self.ended = False
        self.evo_rdy = False
        self.crit = False


    # ---------------- INIT ----------------

    async def initialize(self):
        await self._load_icons()
        await self._load_pokedex()
        await self._load_user()

    async def _load_icons(self):
        data = await icons.find({})
        self.icon = dict((e["type"], e["icon"]) for e in data)
        self.anim = dict((e["type"], e["animated"]) for e in data)

    async def _load_pokedex(self):
        self.choices = await pokedex.find({})
        self.weights = list(p["weight"] for p in self.choices)

    async def _load_user(self):
        data = await daily.find_one(self.query) or {}
        self.badges = len(data.get("user_badges", []))


    # ---------------- ENCOUNTER ----------------

    async def start_encounter(self, i):
        await self.reply(i, UI["random"], btns=False)
        await sleep(0.4)

        await self.pick_pokemon()

        embed = await self.encounter_embed()
        await self.reply(i, self.render_pkmn(), embed)

    def _reset_state(self):
        self.tries  = 0
        self.caught = False
        self.crit   = False
        self.bonus  = ""

    def _set_current(self):
        p = self.pkmn
        self.p_id    = p["item_numb"]
        self.p_name  = p["emoji_name"]
        self.p_emoji = f"<a:{self.p_name}:{p['emoji_id']}>"

    async def pick_pokemon(self):
        self.pkmn, = pyrandom.choices(
            self.choices, weights=self.weights, k=1
        )
        self.max_tries, = pyrandom.choices(
            ATTEMPT_VALUES, weights=ATTEMPT_WEIGHTS, k=1
        )
        self._set_current()
        self._reset_state()


    # ---------------- HELPERS ----------------

    async def reply(self, i, content, embed=None, btns=True):
        respond_interaction(
            i,
            content,
            embed=embed,
            components=self if btns else None
        )

    def embed(self, text, color=0x248046):
        return Message(description=text, color=color)

    def render_pkmn(self):
        e = self.anim.get(self.pkmn["types"][0], "🌲")
        return f"{e} {self.p_emoji} {e}"

    def xp_bar(self, cur, total, size=10):
        if not total:
            return "▱" * size
        fill = round(cur / total * size)
        return "▰" * fill + "▱" * (size - fill)

    def get_types(self, types):
        return " ".join(
            f"{self.icon.get(t,'')} {t}" for t in types
        )

    def get_rarity(self):
        w = self.pkmn["weight"]
        if w >= 0.6: return "Très Commun"
        if w >= 0.5: return "Commun"
        if w >= 0.4: return "Peu commun"
        if w >= 0.3: return "Rare"
        if w >= 0.2: return "Très Rare"
        if w >= 0.1: return "Mythique ✨"
        return "Légendaire 👑"


    # ---------------- CATCH RATE ----------------

    def get_catch_rate(self):
        w = self.pkmn["weight"]

        chance = (w ** 1.2) * 0.5
        chance += min((self.tries - 1) * 0.03, 0.1)

        return max(0.06, min(chance, 0.4))

    def resolve(self, roll, chance):
        crit_ch = min(0.15, 0.003 * self.badges)
        crit = pyrandom.random() < crit_ch

        return (roll < chance) or crit, crit


    # ---------------- EMBEDS ----------------

    async def encounter_embed(self):
        status = await self.is_caught()
        types = self.get_types(self.pkmn["types"])

        return self.embed(
            "-# 🌿 Nouveau Pokémon sauvage !\n\n"
            f"🔹*N°{self.p_id} :* {status} **{self.p_name}**\n"
            f"🔹*Types :* {types}\n"
            f"🔹*Rareté :* {self.get_rarity()}"
        )

    async def is_caught(self):
        item = await items.find_one({
            "item_numb": self.p_id,
            **self.query
        })
        return UI["caught"] if item else UI["uncaught"]

    async def win_text(self, name):
        intro = "💥 Capture Critique !\n" if self.crit else ""
        txt = f"{intro}Et Hop !\n{name} a été capturé ✨"

        txt += await self.update_challenge()
        txt += await self.add_daycare_xp()

        return txt

    async def result_embed(self):
        name = f"**{self.p_name}** {self.p_emoji}"

        if self.caught:
            txt = await self.win_text(name)
            content, color = UI["success"], 0xFFAC32
        else:
            txt = f"Oh Non...\nLe {name} s'enfuit ! 😔"
            content, color = UI["fled"], 0xFF0000

        if self.bonus:
            txt += f"\n\n🎁 Cadeau Bonus : {self.bonus}"

        if self.ended:
            txt += (
                "\n\n⏰ Fin de la chasse.\n"
                f"🏆 Pokémon capturés : **{self.captures}**"
            )

        return self.embed(txt, color), content


    # ---------------- BALL FLOW ----------------

    async def throw_ball(self, i):
        roll = pyrandom.random()
        chance = self.get_catch_rate()

        caught, crit = self.resolve(roll, chance)
        self.caught, self.crit = caught, crit

        delay = self.calc_delay(roll, chance)

        emb = self.embed(
            f"{i.user.mention}\n"
            "lance une **Poké Ball** !",
            0x0094FF
        )

        await self.reply(i, UI["waiting"], emb, False)
        await sleep(delay)

    def calc_delay(self, roll, chance):
        delta = chance - roll
        if self.crit:     return 1.2
        if self.caught:   return 3.5
        if delta > -0.06: return 2.6
        return 1.6

    async def failed_try(self, i):
        txt = pyrandom.choice(FAIL_TEXT)
        emb = self.embed(f"❌ **Échec**\n{txt}", 0xFF0000)
        await self.reply(i, self.render_pkmn(), emb)


    # ---------------- END FLOW ----------------

    async def end_capture(self, i):
        if self.caught:
            await self.save_item()

        if self.passes <= 0 or self.balls <= 0:
            self.ended = True

        self.bonus = self.try_bonus()

        emb, content = await self.result_embed()

        self.update_btns(lock_capture=True)
        await self.reply(i, content, emb)

        if self.ended and not self.evo_rdy:
            self.stop()

    async def save_item(self):
        await items.update_one(
            {"item_numb": self.p_id, **self.query},
            {"$inc": {"count": 1}},
            upsert=True
        )

    def try_bonus(self):
        if self.ended:
            return ""

        r = pyrandom.random()

        if r < 0.04:
            self.balls += 1
            return "**+1 Pokéball**"

        if r < 0.08:
            self.passes += 1
            return "**+1 Passe**"

        return ""


    # ---------------- BUTTONS ----------------

    async def verify(self, i):
        if i.user.id != self.user_id:
            return False
        await defer(i, action="update")
        return True


    def update_btns(self, lock_capture=False):
        self.update_component(
            "ball",
            label=f"Poké Balls ({self.balls})",
            hidden=self.evo_rdy,
            is_disabled=lock_capture
        )

        self.update_component(
            "skip",
            label=f"Passes ({self.passes})",
            hidden=self.evo_rdy,
            is_disabled=self.passes <= 0 or self.ended
        )

        self.update_component(
            "evo",
            hidden=not self.evo_rdy
        )


    # ---------------- BUTTON ACTIONS ----------------

    @button(
        label=f"Poké Balls ({MAX_BALLS})",
        style="success", 
        custom_id="ball"
    )
    async def capture(self, i, b):
        if not await self.verify(i):
            return

        self.tries += 1
        self.balls -= 1

        self.update_btns()
        await self.throw_ball(i)

        if self.caught:
            self.captures += 1
            return await self.end_capture(i)

        if self.tries >= self.max_tries or self.balls <= 0:
            return await self.end_capture(i)

        await self.failed_try(i)


    @button(
        label=f"Passes ({MAX_PASSES})",
        style="primary", 
        custom_id="skip"
    )
    async def skip(self, i, b):
        if not await self.verify(i):
            return

        self.passes -= 1
        self.update_btns()

        await self.start_encounter(i)


    @button(
        label="Évolution", 
        style="success", 
        hidden=True, 
        custom_id="evo"
    )
    async def evolve(self, i, b):
        if not await self.verify(i):
            return

        self.evo_rdy = False
        self.update_btns(lock_capture=True)

        await self.evolve_anim(i, self.evo_data, self.evo)

        if self.ended:
            self.stop()


    # ---------------- DAYCARE ----------------

    async def add_daycare_xp(self):
        data = await daycare.find_one(self.query)
        if not data:
            return ""

        gain = pyrandom.randint(10, 20)

        total = data["xp"] + gain
        req = data["xp_required"]

        await daycare.update_one(
            self.query, {"$inc": {"xp": gain}}
        )

        if total >= req:
            self.evo_rdy = True
            self.evo_data = data
            await self.pick_evolved(data)

        bar = self.xp_bar(total, req)

        txt = (
            f"\n\n🆙 **{data['emoji_name']}** gagne **{gain} XP**\n"
            f"{bar} ({min(total, req)}/{req})"
        )

        if self.evo_rdy:
            txt += "\n-# 🚀 Prêt à évoluer !"

        return txt


    async def pick_evolved(self, data):
        await daycare.delete_one(self.query)

        self.evo = await pokedex.find_one({
            "item_numb": pyrandom.choice(data["evolution_numb"])
        })

        await items.update_one(
            {"item_numb": self.evo["item_numb"], **self.query},
            {"$inc": {"count": 1}},
            upsert=True
        )


    # ---------------- EVOLUTION ----------------

    async def evolve_anim(self, i, data, evo):
        old = data["emoji_name"]
        new = evo["emoji_name"]
        glow = UI["shiny"]

        frames = [
            f"<a:{old}:{data['emoji_id']}>",
            f"<a:{new}:{evo['emoji_id']}>"
        ]

        async def animate(idx, _):
            final = idx == 5

            desc = (
                f"Ton **{old}** évolue en...\n# {new} ! 🎉"
                if final else
                f"Quoi ?? {old} évolue !"
            )

            content = f"{glow}{frames[idx & 1]}{glow}"
            embed = self.embed(desc, 0xFFAC32)
            btns = self if final else False

            await self.reply(i, content, embed, btns)
            await sleep(0.35)

        loop(6, animate)


view = PokeCatch(user.id)
await view.initialize()
await view.start_encounter(interaction)

exit_code = await view.wait(scope=scope, timeout=600)
if exit_code == "timeout":
    await view.reply(interaction, "⏳ Timeout...", btns=False)
    view.stop()
Editor is loading...
Leave a Comment