Implement round preparation
This commit is contained in:
		
							
								
								
									
										180
									
								
								orochi/bot.py
									
									
									
									
									
								
							
							
						
						
									
										180
									
								
								orochi/bot.py
									
									
									
									
									
								
							@@ -1,3 +1,5 @@
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
import disnake
 | 
			
		||||
from disnake import CategoryChannel, PermissionOverwrite, TextChannel
 | 
			
		||||
from disnake.ext import commands
 | 
			
		||||
@@ -5,7 +7,7 @@ import logging
 | 
			
		||||
 | 
			
		||||
from orochi import http
 | 
			
		||||
from orochi.config import Config
 | 
			
		||||
from orochi.models import Game, GameState, Player, RoundVote, Vote
 | 
			
		||||
from orochi.models import Game, GameState, Player, RoundVote, Vote, Round, RoundRoom, Room
 | 
			
		||||
 | 
			
		||||
bot = commands.Bot(command_prefix='!')
 | 
			
		||||
 | 
			
		||||
@@ -221,15 +223,16 @@ async def open(ctx: commands.Context):
 | 
			
		||||
        await ctx.reply("Les votes sont déjà ouverts.")
 | 
			
		||||
        return
 | 
			
		||||
    elif game.state == GameState.RESULTS:
 | 
			
		||||
        await ctx.reply("Les votes viennent d'être fermés, merci de démarrer un nouveau tour avec !prepare.")
 | 
			
		||||
        await ctx.reply("Les votes viennent d'être fermés, merci de démarrer un nouveau tour avec `!prepare`.")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Ensure that each room is configured
 | 
			
		||||
    for room in current_round.rooms:
 | 
			
		||||
        if room is None:
 | 
			
		||||
            await ctx.reply("Les salles ne sont pas configurées.")
 | 
			
		||||
        if len(list(room.players)) != 3:
 | 
			
		||||
            await ctx.reply(f"La salle {room.room.value} ne contient pas trois joueurs, merci de la reconfigurer.")
 | 
			
		||||
        if len(list(room.players)) != 3 or not all(player for player in room.players):
 | 
			
		||||
            return await ctx.reply(f"La salle {room.room.value} ne contient pas trois joueurs, "
 | 
			
		||||
                                   f"merci de finir sa configuration avec `!setup {room.room.value}`.")
 | 
			
		||||
 | 
			
		||||
    # Send messages to players
 | 
			
		||||
    for room in current_round.rooms:
 | 
			
		||||
@@ -267,13 +270,15 @@ async def close(ctx: commands.Context):
 | 
			
		||||
        await ctx.reply("Les votes ne sont pas ouverts.")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    game.state = GameState.RESULTS
 | 
			
		||||
 | 
			
		||||
    current_round = game.rounds[-1]
 | 
			
		||||
    for room in current_round.rooms:
 | 
			
		||||
        for vote in room.votes:
 | 
			
		||||
            if vote.vote is None:
 | 
			
		||||
                vote.vote = Vote.ALLY
 | 
			
		||||
                await ctx.send(f"L'équipe **{' et '.join(player.name for player in vote.players)}** "
 | 
			
		||||
                               f"n'a pas voté en salle {room.room.value} et s'est alliée par défaut.")
 | 
			
		||||
                               f"n'a pas voté en salle **{room.room.value}** et s'est alliée par défaut.")
 | 
			
		||||
 | 
			
		||||
    for player in game.players.values():
 | 
			
		||||
        channel = bot.get_channel(player.private_channel_id)
 | 
			
		||||
@@ -283,10 +288,79 @@ async def close(ctx: commands.Context):
 | 
			
		||||
            await channel.send("Tiens ! Vous êtes morts :)")
 | 
			
		||||
 | 
			
		||||
    await ctx.reply("Les votes ont bien été fermés.")
 | 
			
		||||
    game.state = GameState.RESULTS
 | 
			
		||||
    game.save('game.save')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bot.command(help="Préparation du tour suivant")
 | 
			
		||||
@commands.has_permissions(administrator=True)
 | 
			
		||||
async def prepare(ctx: commands.Context):
 | 
			
		||||
    game: Game = Game.INSTANCE
 | 
			
		||||
    if game.state != GameState.RESULTS:
 | 
			
		||||
        await ctx.reply("Le tour actuel n'est pas terminé.")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    game.state = GameState.PREPARING
 | 
			
		||||
    game.rounds.append(Round(
 | 
			
		||||
        round=len(game.rounds) + 1,
 | 
			
		||||
        room_a=RoundRoom(
 | 
			
		||||
            room=Room.A,
 | 
			
		||||
            vote1=RoundVote(),
 | 
			
		||||
            vote2=RoundVote(),
 | 
			
		||||
        ),
 | 
			
		||||
        room_b=RoundRoom(
 | 
			
		||||
            room=Room.B,
 | 
			
		||||
            vote1=RoundVote(),
 | 
			
		||||
            vote2=RoundVote(),
 | 
			
		||||
        ),
 | 
			
		||||
        room_c=RoundRoom(
 | 
			
		||||
            room=Room.C,
 | 
			
		||||
            vote1=RoundVote(),
 | 
			
		||||
            vote2=RoundVote(),
 | 
			
		||||
        ),
 | 
			
		||||
    ))
 | 
			
		||||
    game.save('game.save')
 | 
			
		||||
 | 
			
		||||
    await ctx.reply("Le tour suivant est en préparation. Utilisez `!setup A|B|C` pour paramétrer les salles A, B ou C. "
 | 
			
		||||
                    "Dan peut faire la même chose.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bot.command(help="Prévisualisation des combats d'un tour")
 | 
			
		||||
@commands.has_any_role('IA', 'Dan')
 | 
			
		||||
async def preview(ctx: commands.Context):
 | 
			
		||||
    game: Game = Game.INSTANCE
 | 
			
		||||
    current_round = game.rounds[-1]
 | 
			
		||||
    for room in current_round.rooms:
 | 
			
		||||
        await ctx.send(f"Dans la salle **{room.room.value}**, s'affronteront :\n"
 | 
			
		||||
                       f"- **{' et '.join(str(player or '_personne_') for player in room.vote1.players)}**\n"
 | 
			
		||||
                       f"- **{' et '.join(str(player or '_personne_') for player in room.vote2.players)}**")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@bot.command(help="Préparation d'une salle")
 | 
			
		||||
@commands.has_any_role('IA', 'Dan')
 | 
			
		||||
async def setup(ctx: commands.Context, room: Room):
 | 
			
		||||
    game: Game = Game.INSTANCE
 | 
			
		||||
    current_round = game.rounds[-1]
 | 
			
		||||
 | 
			
		||||
    if game.state != GameState.PREPARING:
 | 
			
		||||
        return await ctx.reply("Vous ne pouvez pas préparer la salle avant le tour suivant.")
 | 
			
		||||
 | 
			
		||||
    await ctx.reply(f"Préparation de la salle {room.value}.")
 | 
			
		||||
 | 
			
		||||
    match room:
 | 
			
		||||
        case Room.A:
 | 
			
		||||
            round_room = current_round.room_a
 | 
			
		||||
        case Room.B:
 | 
			
		||||
            round_room = current_round.room_b
 | 
			
		||||
        case _:
 | 
			
		||||
            round_room = current_round.room_c
 | 
			
		||||
 | 
			
		||||
    view = PrepareRoomView(round_room, 0, timeout=300)
 | 
			
		||||
    await ctx.send(f"Veuillez choisir qui s'affrontera seul dans la salle **{room.value}**.", view=view)
 | 
			
		||||
 | 
			
		||||
    if round_room.vote1.player1 is not None:
 | 
			
		||||
        await ctx.send(f"Attention : **{round_room.vote1.player1.name}** est déjà choisie.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VoteView(disnake.ui.View):
 | 
			
		||||
    @disnake.ui.button(label="S'allier", style=disnake.ButtonStyle.green)
 | 
			
		||||
    async def confirm(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):
 | 
			
		||||
@@ -330,6 +404,100 @@ class VoteView(disnake.ui.View):
 | 
			
		||||
            game.save('game.save')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PrepareRoomView(disnake.ui.View):
 | 
			
		||||
    def __init__(self, round_room: RoundRoom, player_id: int, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        assert 0 <= player_id < 3
 | 
			
		||||
        self.round_room = round_room
 | 
			
		||||
        self.player_id = player_id
 | 
			
		||||
 | 
			
		||||
        for player_name in Config.PLAYERS:
 | 
			
		||||
            async def choose_player(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):
 | 
			
		||||
                game: Game = Game.INSTANCE
 | 
			
		||||
                player = game.players[button.label]
 | 
			
		||||
                current_round = game.rounds[-1]
 | 
			
		||||
                for room in current_round.rooms:
 | 
			
		||||
                    for vote in room.votes:
 | 
			
		||||
                        if player in vote.players:
 | 
			
		||||
                            replaced_player = None
 | 
			
		||||
                            if room == self.round_room:
 | 
			
		||||
                                match self.player_id:
 | 
			
		||||
                                    case 0:
 | 
			
		||||
                                        replaced_player = room.vote1.player1
 | 
			
		||||
                                    case 1:
 | 
			
		||||
                                        replaced_player = room.vote2.player1
 | 
			
		||||
                                    case 2:
 | 
			
		||||
                                        replaced_player = room.vote2.player2
 | 
			
		||||
 | 
			
		||||
                            if replaced_player != player:
 | 
			
		||||
                                await interaction.send(
 | 
			
		||||
                                    f"Attention : **{player.name}** était déjà attribué⋅e dans la salle "
 | 
			
		||||
                                    f"**{room.room.value}**. Vous devrez probablement la re-configurer.")
 | 
			
		||||
 | 
			
		||||
                        if vote.player1 == player:
 | 
			
		||||
                            vote.player1 = None
 | 
			
		||||
                        elif vote.player2 == player:
 | 
			
		||||
                            vote.player2 = None
 | 
			
		||||
 | 
			
		||||
                self.clear_items()
 | 
			
		||||
                await interaction.send("Choix bien pris en compte.")
 | 
			
		||||
                await interaction.edit_original_message(view=self)
 | 
			
		||||
                self.stop()
 | 
			
		||||
 | 
			
		||||
                match self.player_id:
 | 
			
		||||
                    case 0:
 | 
			
		||||
                        self.round_room.vote1.player1 = player
 | 
			
		||||
 | 
			
		||||
                        view = PrepareRoomView(self.round_room, 1, timeout=300)
 | 
			
		||||
                        await interaction.send(
 | 
			
		||||
                            f"**{player.name}** se battra seul⋅e. Veuillez désormais choisir contre qui "
 | 
			
		||||
                            "il ou elle se battra.", view=view)
 | 
			
		||||
                        if self.round_room.vote2.player1 is not None:
 | 
			
		||||
                            await interaction.send(
 | 
			
		||||
                                f"Attention : **{self.round_room.vote2.player1.name}** est déjà choisi⋅e."
 | 
			
		||||
                            )
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        self.round_room.vote2.player1 = player
 | 
			
		||||
 | 
			
		||||
                        view = PrepareRoomView(self.round_room, 2, timeout=300)
 | 
			
		||||
                        await interaction.send(
 | 
			
		||||
                            f"**{player.name}** se battra contre **{self.round_room.vote1.player1.name}**. "
 | 
			
		||||
                            "Veuillez désormais choisir son partenaire.",
 | 
			
		||||
                            view=view)
 | 
			
		||||
                        if self.round_room.vote2.player2 is not None:
 | 
			
		||||
                            await interaction.send(
 | 
			
		||||
                                f"Attention : **{self.round_room.vote2.player2.name}** est déjà choisi⋅e."
 | 
			
		||||
                            )
 | 
			
		||||
                    case 2:
 | 
			
		||||
                        self.round_room.vote2.player2 = player
 | 
			
		||||
                        await interaction.send(
 | 
			
		||||
                            f"Dans la salle **{round_room.room.value}**, **{round_room.vote1.player1.name}** "
 | 
			
		||||
                            f"affrontera **{round_room.vote2.player1.name}** et **{round_room.vote2.player2.name}**.")
 | 
			
		||||
                        await interaction.send(
 | 
			
		||||
                            "Vous pouvez redéfinir la salle tant que le tour n'est pas lancé. "
 | 
			
		||||
                            "Utilisez !preview pour un récapitulatif des tours.")
 | 
			
		||||
 | 
			
		||||
                game.save('game.save')
 | 
			
		||||
 | 
			
		||||
            game: Game = Game.INSTANCE
 | 
			
		||||
            current_round = game.rounds[-1]
 | 
			
		||||
            for room in current_round.rooms:
 | 
			
		||||
                for player in room.players:
 | 
			
		||||
                    if player is not None and player.name == player_name:
 | 
			
		||||
                        style = disnake.ButtonStyle.danger
 | 
			
		||||
                        break
 | 
			
		||||
                else:
 | 
			
		||||
                    continue
 | 
			
		||||
                break
 | 
			
		||||
            else:
 | 
			
		||||
                style = disnake.ButtonStyle.primary
 | 
			
		||||
 | 
			
		||||
            button = disnake.ui.Button(style=style, label=player_name)
 | 
			
		||||
            button.callback = partial(choose_player, self, button)
 | 
			
		||||
            button._view = self
 | 
			
		||||
            self.add_item(button)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run():
 | 
			
		||||
    config = Config.load()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import pickle
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from enum import Enum
 | 
			
		||||
from typing import ClassVar, Iterable, Generator
 | 
			
		||||
 | 
			
		||||
@@ -62,13 +61,15 @@ class Player:
 | 
			
		||||
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class RoundVote:
 | 
			
		||||
    player1: Player
 | 
			
		||||
    player1: Player | None = None
 | 
			
		||||
    player2: Player | None = None
 | 
			
		||||
    vote: Vote | None = None
 | 
			
		||||
    timestamp: datetime | None = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def players(self) -> Iterable[Player]:
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
        {% for player in game.players.values() %}
 | 
			
		||||
            <tr>
 | 
			
		||||
                <td>{{ player.name }}</td>
 | 
			
		||||
                <td>{{ player.score }}</td>
 | 
			
		||||
                <td>{{ player.score }}{% if player.score <= 0 %} ☠️ {% elif player.score >= 9 %} 9️⃣ {% endif %}</td>
 | 
			
		||||
            </tr>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        </tbody>
 | 
			
		||||
@@ -44,7 +44,7 @@
 | 
			
		||||
        <li>
 | 
			
		||||
            <b>État :</b>
 | 
			
		||||
            {% if game.state.value == 0 %}
 | 
			
		||||
                <span style="color: yellow;">En préparation ...</span>
 | 
			
		||||
                <span style="color: orange;">En préparation ...</span>
 | 
			
		||||
            {% elif game.state.value == 1 %}
 | 
			
		||||
                <span style="color: red;">Votes en cours ...</span>
 | 
			
		||||
            {% else %}
 | 
			
		||||
@@ -55,7 +55,7 @@
 | 
			
		||||
 | 
			
		||||
    <h2>Récapitulatif par tour</h2>
 | 
			
		||||
 | 
			
		||||
    {% for round in game.rounds %}
 | 
			
		||||
    {% for round in game.rounds if admin or game.state.value > 0 or round.round < game.rounds|length %}
 | 
			
		||||
        <h3>Tour n°{{ round.round }}</h3>
 | 
			
		||||
 | 
			
		||||
        <table>
 | 
			
		||||
@@ -74,9 +74,9 @@
 | 
			
		||||
                    {% if loop.index0 == 0 %}
 | 
			
		||||
                        <td rowspan="2">{{ room.room.value }}</td>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    <td>{{ vote.player1.name }}{% if vote.player2 %}, {{ vote.player2.name }}{% endif %}</td>
 | 
			
		||||
                    <td>{{ vote.player1.name|default('<em>personne</em>')|safe }}{% if vote.player2 %}, {{ vote.player2.name }}{% endif %}</td>
 | 
			
		||||
                    {% if round.round != game.rounds|length or game.state.value == 2 or admin %}
 | 
			
		||||
                        <td>{{ vote.vote.value|default('Pas de vote') }}</td>
 | 
			
		||||
                        <td>{{ vote.vote.value|default('<em>Pas de vote</em>')|safe }}</td>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <td><em>Vote en cours ...</em></td>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user