Implement round preparation

This commit is contained in:
Yohann D'ANELLO 2021-11-09 00:29:07 +01:00
parent de6dc03dda
commit 388c729235
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
3 changed files with 183 additions and 14 deletions

View File

@ -1,3 +1,5 @@
from functools import partial
import disnake import disnake
from disnake import CategoryChannel, PermissionOverwrite, TextChannel from disnake import CategoryChannel, PermissionOverwrite, TextChannel
from disnake.ext import commands from disnake.ext import commands
@ -5,7 +7,7 @@ import logging
from orochi import http from orochi import http
from orochi.config import Config 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='!') bot = commands.Bot(command_prefix='!')
@ -221,15 +223,16 @@ async def open(ctx: commands.Context):
await ctx.reply("Les votes sont déjà ouverts.") await ctx.reply("Les votes sont déjà ouverts.")
return return
elif game.state == GameState.RESULTS: 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 return
# Ensure that each room is configured # Ensure that each room is configured
for room in current_round.rooms: for room in current_round.rooms:
if room is None: if room is None:
await ctx.reply("Les salles ne sont pas configurées.") await ctx.reply("Les salles ne sont pas configurées.")
if len(list(room.players)) != 3: if len(list(room.players)) != 3 or not all(player for player in room.players):
await ctx.reply(f"La salle {room.room.value} ne contient pas trois joueurs, merci de la reconfigurer.") 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 # Send messages to players
for room in current_round.rooms: 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.") await ctx.reply("Les votes ne sont pas ouverts.")
return return
game.state = GameState.RESULTS
current_round = game.rounds[-1] current_round = game.rounds[-1]
for room in current_round.rooms: for room in current_round.rooms:
for vote in room.votes: for vote in room.votes:
if vote.vote is None: if vote.vote is None:
vote.vote = Vote.ALLY vote.vote = Vote.ALLY
await ctx.send(f"L'équipe **{' et '.join(player.name for player in vote.players)}** " 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(): for player in game.players.values():
channel = bot.get_channel(player.private_channel_id) 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 channel.send("Tiens ! Vous êtes morts :)")
await ctx.reply("Les votes ont bien été fermés.") await ctx.reply("Les votes ont bien été fermés.")
game.state = GameState.RESULTS
game.save('game.save') 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): class VoteView(disnake.ui.View):
@disnake.ui.button(label="S'allier", style=disnake.ButtonStyle.green) @disnake.ui.button(label="S'allier", style=disnake.ButtonStyle.green)
async def confirm(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction): async def confirm(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):
@ -330,6 +404,100 @@ class VoteView(disnake.ui.View):
game.save('game.save') 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(): def run():
config = Config.load() config = Config.load()

View File

@ -1,6 +1,5 @@
import pickle import pickle
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum from enum import Enum
from typing import ClassVar, Iterable, Generator from typing import ClassVar, Iterable, Generator
@ -62,13 +61,15 @@ class Player:
return s return s
def __str__(self):
return self.name
@dataclass @dataclass
class RoundVote: class RoundVote:
player1: Player player1: Player | None = None
player2: Player | None = None player2: Player | None = None
vote: Vote | None = None vote: Vote | None = None
timestamp: datetime | None = None
@property @property
def players(self) -> Iterable[Player]: def players(self) -> Iterable[Player]:

View File

@ -30,7 +30,7 @@
{% for player in game.players.values() %} {% for player in game.players.values() %}
<tr> <tr>
<td>{{ player.name }}</td> <td>{{ player.name }}</td>
<td>{{ player.score }}</td> <td>{{ player.score }}{% if player.score <= 0 %} ☠️ {% elif player.score >= 9 %} 9⃣ {% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -44,7 +44,7 @@
<li> <li>
<b>État :</b> <b>État :</b>
{% if game.state.value == 0 %} {% 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 %} {% elif game.state.value == 1 %}
<span style="color: red;">Votes en cours ...</span> <span style="color: red;">Votes en cours ...</span>
{% else %} {% else %}
@ -55,7 +55,7 @@
<h2>Récapitulatif par tour</h2> <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> <h3>Tour n°{{ round.round }}</h3>
<table> <table>
@ -74,9 +74,9 @@
{% if loop.index0 == 0 %} {% if loop.index0 == 0 %}
<td rowspan="2">{{ room.room.value }}</td> <td rowspan="2">{{ room.room.value }}</td>
{% endif %} {% 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 %} {% 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 %} {% else %}
<td><em>Vote en cours ...</em></td> <td><em>Vote en cours ...</em></td>
{% endif %} {% endif %}