Implement round preparation
This commit is contained in:
parent
de6dc03dda
commit
388c729235
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 %}
|
||||
|
|
Loading…
Reference in New Issue