Implement votes

This commit is contained in:
Yohann D'ANELLO 2021-11-08 21:18:48 +01:00
parent caa4bcfa92
commit 8f939e288b
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
3 changed files with 104 additions and 21 deletions

View File

@ -5,7 +5,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 from orochi.models import Game, GameState, Player, RoundVote, Vote
bot = commands.Bot(command_prefix='!') bot = commands.Bot(command_prefix='!')
@ -182,6 +182,20 @@ async def on_ready():
config.save() config.save()
@bot.command(help="Sauvegarde la partie")
@commands.has_permissions(administrator=True)
async def save(ctx: commands.Context):
Game.INSTANCE.save('game.save')
await ctx.reply("La partie a été sauvegardée.")
@bot.command(help="Recharger la partie")
@commands.has_permissions(administrator=True)
async def load(ctx: commands.Context):
Game.load('game.save')
await ctx.reply("La partie a été rechargée.")
@bot.command(help="Envoyer un message en tant qu'Orochi.") @bot.command(help="Envoyer un message en tant qu'Orochi.")
@commands.has_permissions(administrator=True) @commands.has_permissions(administrator=True)
async def send(ctx: commands.Context, *, message: str): async def send(ctx: commands.Context, *, message: str):
@ -197,26 +211,88 @@ async def brother(ctx: commands.Context, *, message: str):
await ctx.message.reply("Message envoyé.") await ctx.message.reply("Message envoyé.")
@bot.command() @bot.command(help="Ouvrir les votes")
async def vote(ctx: commands.Context): async def open(ctx: commands.Context):
view = Confirm() game: Game = Game.INSTANCE
await ctx.message.reply("plop", view=view) current_round = game.rounds[-1]
await view.wait()
if game.state == GameState.VOTING:
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.")
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.")
# Send messages to players
for room in current_round.rooms:
votes = list(room.votes)
for i, vote in enumerate(votes):
players = list(vote.players)
other_vote = votes[1 - i]
for j, player in enumerate(players):
other_player = players[1 - j] if len(players) == 2 else None
view = VoteView(timeout=3600)
channel_id = player.private_channel_id
channel = bot.get_channel(channel_id)
message = "Les votes sont ouverts.\n"
message += f"Vous devez aller voter en salle **{room.room.value}**.\n"
if other_player:
message += f"Vous êtes allié⋅e avec **{other_player.name}**.\n"
message += f"Vous affrontez {' et '.join(f'**{adv.name}**' for adv in other_vote.players)}.\n"
message += "Bonne chance !"
await channel.send(message)
await channel.send("Pour voter, utilisez l'un des boutons ci-dessous. Vous pouvez appuyer "
"sur le bouton plusieurs fois, mais seul le premier vote sera enregistré.",
view=view)
game.state = GameState.VOTING
await ctx.reply("Les salles de vote sont ouvertes, les joueur⋅se⋅s peuvent désormais voter.")
# Define a simple View that gives us a confirmation menu class VoteView(disnake.ui.View):
class Confirm(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):
self.clear_items() await interaction.response.send_message("Votre vote a bien été pris en compte.", ephemeral=True)
await interaction.response.edit_message(content="Vous vous êtes allié.", view=self)
self.stop() self.vote(interaction, Vote.ALLY)
@disnake.ui.button(label="Trahir", style=disnake.ButtonStyle.red) @disnake.ui.button(label="Trahir", style=disnake.ButtonStyle.red)
async def cancel(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction): async def cancel(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):
self.clear_items() await interaction.response.send_message("Votre vote a bien été pris en compte.", ephemeral=True)
await interaction.response.edit_message(content="Vous avez trahi.", view=self)
self.stop() self.vote(interaction, Vote.BETRAY)
def vote(self, interaction: disnake.MessageInteraction, vote: Vote) -> None:
game = Game.INSTANCE
current_round = game.rounds[-1]
current_player: Player | None = None
for player in game.players.values():
if player.private_channel_id == interaction.channel_id:
current_player = player
break
current_vote: RoundVote | None = None
for room in current_round.rooms:
for v in room.votes:
if current_player in v.players:
current_vote = v
break
else:
continue
break
if current_vote.vote is None:
current_vote.vote = vote
game.save('game.save')
def run(): def run():

View File

@ -2,7 +2,7 @@ import pickle
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum
from typing import ClassVar from typing import ClassVar, Iterable, Generator
class Room(Enum): class Room(Enum):
@ -28,7 +28,7 @@ class Player:
private_channel_id: int = field(hash=False) private_channel_id: int = field(hash=False)
@property @property
def round_votes(self): def round_votes(self) -> Generator["RoundVote", None, None]:
for r in Game.INSTANCE.rounds: for r in Game.INSTANCE.rounds:
for room in r.rooms: for room in r.rooms:
for vote in room.votes: for vote in room.votes:
@ -36,7 +36,7 @@ class Player:
yield vote yield vote
@property @property
def score(self): def score(self) -> int:
s = 3 s = 3
for vote in self.round_votes: for vote in self.round_votes:
@ -63,11 +63,13 @@ class RoundVote:
timestamp: datetime | None = None timestamp: datetime | None = None
@property @property
def players(self): def players(self) -> Iterable[Player]:
if self.player2 is None:
return self.player1,
return self.player1, self.player2 return self.player1, self.player2
@property @property
def room(self): def room(self) -> "RoundRoom":
for r in Game.INSTANCE.rounds: for r in Game.INSTANCE.rounds:
for room in r.rooms: for room in r.rooms:
if self in room.votes: if self in room.votes:
@ -84,6 +86,11 @@ class RoundRoom:
def votes(self): def votes(self):
return self.vote1, self.vote2 return self.vote1, self.vote2
@property
def players(self) -> Generator[Player, None, None]:
for vote in self.votes:
yield from vote.players
@property @property
def round(self): def round(self):
for r in Game.INSTANCE.rounds: for r in Game.INSTANCE.rounds:
@ -99,7 +106,7 @@ class Round:
room_c: RoundRoom room_c: RoundRoom
@property @property
def rooms(self): def rooms(self) -> tuple[RoundRoom, RoundRoom, RoundRoom]:
return self.room_a, self.room_b, self.room_c return self.room_a, self.room_b, self.room_c

View File

@ -59,7 +59,7 @@
{% endif %} {% endif %}
<td>{{ vote.player1.name }}{% if vote.player2 %}, {{ vote.player2.name }}{% endif %}</td> <td>{{ vote.player1.name }}{% if vote.player2 %}, {{ vote.player2.name }}{% endif %}</td>
{% if round.round != game.rounds|length or admin %} {% if round.round != game.rounds|length or admin %}
<td>{{ room.vote1.vote.value|default('Pas de vote') }}</td> <td>{{ vote.vote.value|default('Pas de vote') }}</td>
{% else %} {% else %}
<td><em>Vote en cours ...</em></td> <td><em>Vote en cours ...</em></td>
{% endif %} {% endif %}