commit 5cdc9fdf82ebcddb3d455ccd3fc7d594feebc257 Author: Terrible Entry Date: Tue Jun 16 13:15:22 2026 +0200 Initial commit: Werewolf game with voice prompts diff --git a/werewoolf/.gitignore b/werewoolf/.gitignore new file mode 100644 index 0000000..773fe78 --- /dev/null +++ b/werewoolf/.gitignore @@ -0,0 +1,218 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock +#poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +#pdm.lock +#pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +#pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml +# custom +voicesline/playersname \ No newline at end of file diff --git a/werewoolf/finger_count.py b/werewoolf/finger_count.py new file mode 100644 index 0000000..8694571 --- /dev/null +++ b/werewoolf/finger_count.py @@ -0,0 +1,87 @@ +# code inspiration from https://www.geekering.com/categories/computer-vision/marcellacavalcanti/hand-tracking-and-finger-counting-in-python-with-mediapipe/ because i dont know how to use mediapipe + +import cv2 +import mediapipe as mp +import time + +mp_drawing = mp.solutions.drawing_utils +mp_drawing_styles = mp.solutions.drawing_styles +mp_hands = mp.solutions.hands + +def count_fingers(stable_duration=2): + previous_finger_count = -1 # Initialize to an invalid count + stable_start_time = None # To track when the count became stable + cap = cv2.VideoCapture(0) + with mp_hands.Hands( + model_complexity=0, + min_detection_confidence=0.5, + min_tracking_confidence=0.5) as hands: + + while cap.isOpened(): + success, image = cap.read() + if not success: + print("Ignoring empty camera frame.") + continue + + image.flags.writeable = False + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + results = hands.process(image) + + image.flags.writeable = True + image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) + + fingerCount = 0 # Reset fingerCount for each frame + + if results.multi_hand_landmarks: + for hand_landmarks in results.multi_hand_landmarks: + handIndex = results.multi_hand_landmarks.index(hand_landmarks) + handLabel = results.multi_handedness[handIndex].classification[0].label + + handLandmarks = [] + for landmarks in hand_landmarks.landmark: + handLandmarks.append([landmarks.x, landmarks.y]) + + # Count fingers + if handLabel == "Left" and handLandmarks[4][0] > handLandmarks[3][0]: + fingerCount += 1 + elif handLabel == "Right" and handLandmarks[4][0] < handLandmarks[3][0]: + fingerCount += 1 + + if handLandmarks[8][1] < handLandmarks[6][1]: # Index finger + fingerCount += 1 + if handLandmarks[12][1] < handLandmarks[10][1]: # Middle finger + fingerCount += 1 + if handLandmarks[16][1] < handLandmarks[14][1]: # Ring finger + fingerCount += 1 + if handLandmarks[20][1] < handLandmarks[18][1]: # Pinky + fingerCount += 1 + + # Draw hand landmarks + mp_drawing.draw_landmarks( + image, + hand_landmarks, + mp_hands.HAND_CONNECTIONS, + mp_drawing_styles.get_default_hand_landmarks_style(), + mp_drawing_styles.get_default_hand_connections_style()) + + # Display finger count + cv2.putText(image, str(fingerCount), (50, 450), cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 0, 0), 10) + #Display image + cv2.imshow('Finger Count', image) + + # Check if the finger count is stable + if fingerCount == previous_finger_count and fingerCount != 0: + if stable_start_time is None: + stable_start_time = time.time() # Start the timer + elif time.time() - stable_start_time >= stable_duration and fingerCount != 0: + cap.release() + cv2.destroyAllWindows() + return fingerCount # Return the stable finger count + else: + stable_start_time = None # Reset the timer if the count changes + + previous_finger_count = fingerCount # Update the previous count + if cv2.waitKey(5) & 0xFF == 27: + break + + return None # Return None if the loop ends without a stable count diff --git a/werewoolf/logger.py b/werewoolf/logger.py new file mode 100644 index 0000000..3262df6 --- /dev/null +++ b/werewoolf/logger.py @@ -0,0 +1,31 @@ +import logging + +log = logging.getLogger(__name__) +log.setLevel(logging.INFO) +formatter = logging.Formatter( + "%(asctime)s \033[1m%(levelname)s\033[0m\033[0m %(message)s" +) + +# stdout +stream_handler = logging.StreamHandler() +stream_handler.setLevel(logging.DEBUG) +stream_handler.setFormatter(formatter) +log.addHandler(stream_handler) + + +# Add custom log level names with colors +logging.addLevelName( + logging.DEBUG, "\033[1;30m%s\033[1;0m" % logging.getLevelName(logging.DEBUG) +) # gray +logging.addLevelName( + logging.INFO, "\033[1;34m%s\033[1;0m" % logging.getLevelName(logging.INFO) +) # blue +logging.addLevelName( + logging.WARNING, "\033[1;33m%s\033[1;0m" % logging.getLevelName(logging.WARNING) +) # yellow +logging.addLevelName( + logging.ERROR, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.ERROR) +) # red +logging.addLevelName( + logging.CRITICAL, "\033[1;35m%s\033[1;0m" % logging.getLevelName(logging.CRITICAL) +) # \ No newline at end of file diff --git a/werewoolf/main.py b/werewoolf/main.py new file mode 100644 index 0000000..548c507 --- /dev/null +++ b/werewoolf/main.py @@ -0,0 +1,505 @@ +import random +from typing import Dict, List, Optional +from dataclasses import dataclass +import sys +from logger import log +from finger_count import * +from playsound import playsound +import wave +from piper import PiperVoice +import os +from art import * +from translation import * + + +@dataclass +class Player: + name: str + alias: str + role: str + alive: bool = True + protected: bool = False + + +class Game: + def __init__(self): + # single source of truth: name -> Player + self.players: Dict[str, Player] = {} + + # lovers + self.lovers: Optional[Tuple[str, str]] = None + + # Dead this night + self.dead_this_night: [List[str]] = [] + + # Used potions + self.used_potions: [list[str]] = [] + # Language + self.language: str = "fr" + # ------------------------- + # setup / I/O + # ------------------------- + def load_lists(self) -> Optional[Dict[str, List[str]]]: + """ + Load players and roles from files and return them as lists. + Returns dict with keys 'players' and 'roles'. + """ + players_file = "players.txt" + roles_file = "roles.txt" + words_file = "randoms_words.txt" + log.info("Loading lists...") + + try: + with open(players_file, "r", encoding="utf-8") as pf: + player_names = [line.strip() for line in pf if line.strip()] + except FileNotFoundError: + log.critical("Players file not found: %s", players_file) + return None + + try: + with open(roles_file, "r", encoding="utf-8") as rf: + roles_list = [line.strip() for line in rf if line.strip()] + except FileNotFoundError: + log.critical("Roles file not found: %s", roles_file) + return None + + try: + with open(words_file, "r", encoding="utf-8") as rf: + alias_list = [line.strip() for line in rf if line.strip()] + except FileNotFoundError: + log.critical("Roles file not found: %s", words_file) + return None + + + log.info("Load complete!") + + return {"players": player_names, "roles": roles_list, "alias": alias_list} + + # ------------------------- + # player / role assignment + # ------------------------- + def give_roles_to_players(self, player_names: Optional[List[str]] = None, + roles_list: Optional[List[str]] = None, + alias_list: Optional[List[str]] = None) -> None: + """ + Assign a random role to each player. Accepts optional lists (returned by load_lists). + If lists are not supplied, it will attempt to read files itself. + """ + number = 0 + if not player_names: + log.info("Players are not initialized") + return + if not roles_list: + log.info("Roles are not initialized") + return + if len(roles_list) < len(player_names): + log.error("Not enough roles for players (roles: %d, players: %d)", + len(roles_list), len(player_names)) + return + if len(alias_list) < len(player_names): + log.error("Not enough randoms words for players (words: %d, players: %d)", + len(alias_list), len(player_names)) + return + + available_roles = list(roles_list) + random.shuffle(available_roles) + available_alias = list(alias_list) + random.shuffle(alias_list) + + # clear any existing players (safe to reassign) + self.players.clear() + # clear any player name audio + if not os.path.exists("./voicesline/playersname"): + os.makedirs("./voicesline/playersname") + for filename in os.listdir("./voicesline/playersname"): + file_path = os.path.join("./voicesline/playersname", filename) + # Check if it is a file (not a subdirectory) + if os.path.isfile(file_path): + os.remove(file_path) # Remove the file + for filename in os.listdir("./voicesline/alias"): + file_path = os.path.join("./voicesline/alias", filename) + # Check if it is a file (not a subdirectory) + if os.path.isfile(file_path): + os.remove(file_path) # Remove the file + if self.language == "fr": + voice = PiperVoice.load("./voices/fr/fr_FR-tom-medium.onnx") + elif self.language == "en": + voice = PiperVoice.load("./voices/en/en_GB-alan-medium.onnx") + else: + log.info("Unknow language") + for name in player_names: + chosen_role = available_roles.pop() + chosen_alias = available_alias.pop() + with wave.open(f"./voicesline/playersname/{name}.wav", "wb") as wav_file: + voice.synthesize_wav(name, wav_file) + with wave.open(f"./voicesline/alias/{chosen_alias}.wav", "wb") as wav_file: + voice.synthesize_wav(chosen_alias, wav_file) + number += 1 + self.players[number] = Player(name=name, role=chosen_role, alias=chosen_alias) + self.play_sound("players", name.capitalize()) + self.play_sound("prompts", "first_wakes") + if name == "Dan": + self.play_sound("prompts", "cheater") + self.show_text(chosen_role) + self.show_text(chosen_alias) + time.sleep(3) + os.system('clear') + self.play_sound("players", name.capitalize()) + self.play_sound("prompts", "sleep_again") + + + + + log.debug("Assigned roles:") + for player in self.players.values(): + log.debug("%s: %s", player.name, player.role) + + # ------------------------- + # utilities + # ------------------------- + def clear_data(self) -> None: + for player in self.players: + self.players[player].protected = False + + + def couple(self) -> None: + os.system('clear') + self.play_sound("prompts", "lovers") + log.debug(self.lovers) + for player in self.lovers: + log.info(self.players[player].alias) + self.play_sound("alias", self.players[player].alias) + self.show_text(self.players[player].role) + time.sleep(10) + + def show_text(self, text: str) -> None: + if self.language == "fr": + if text in FR: + Art=text2art(FR.get(text)) + print(Art) + else: + Art=text2art(text) + print(Art) + if self.language == "en": + Art=text2art(text) + print(Art) + + def get_remaining_roles(self) -> List[str]: + remaining_roles = [] + for player in self.players: + if self.players[player].alive: + remaining_roles.append(self.players[player].role) + log.DEBUG(remaining_roles) + return remaining_roles + + def number_2_name(self, player_number: int): + player_name = self.players[player_number].name + return player_name + + def play_sound(self, type, soundinfo) -> None: + if type == "prompts": + f = f"./voicesline/prompts/{self.language}/{soundinfo}.wav" + elif type == "roles": + f = f"./voicesline/roles/{self.language}/{soundinfo}.wav" + elif type == "players": + f = f"./voicesline/playersname/{soundinfo}.wav" + elif type == "alias": + f = f"./voicesline/alias/{soundinfo}.wav" + else: + log.debug("An error occurred") + return + time.sleep(0.2) + playsound(f) + + def select_someone(self, prompt: Optional[str] = None) -> Optional[str]: + """ + Prompt the user to enter a player name until a valid one is entered. + Returns None on EOF/KeyboardInterrupt. + """ + prompt = prompt or "show_number" + #self.play_sound("prompts", prompt) + while True: + selected = count_fingers() + + # exact match + if selected in self.players: + return selected + + def choose_between(self, options): + #Prompt the user to choose between options until a valid one is entered + numbered_options = " ".join([f"{i+1}: {option}" for i, option in enumerate(options)]) + log.info(f"Choose between {numbered_options}:") + while True: + selected = count_fingers() + try: + index = int(selected) - 1 + if 0 <= index < len(options): + chosen_option = options[index] + log.debug("You chose %s!", chosen_option) + return chosen_option + else: + log.warning("Invalid number entered.") + except ValueError: + log.warning("Invalid input. Please enter an option or number.") + # ------------------------- + # game actions + # ------------------------- + def put_in_love(self, player1: str, player2: str) -> None: + """Link two players such that if one dies the other dies too.""" + if player1 not in self.players or player2 not in self.players: + log.error("One or both players do not exist: %s, %s", player1, player2) + return + + self.lovers = (player1, player2) + + log.debug("Players now put in love: %s <-> %s", self.players[player1], self.players[player2]) + + log.info("They are now bounded by love.") + log.info("They wake up and reveal their role to each other.") + log.info("Those two go back to sleep.") + + + def kill(self, player: str) -> None: + """Kill a player.""" + if player not in self.players: + log.error("Couldn't kill unknown player: %s", player) + return + + target = self.players[player] + + # already dead? + if not target.alive: + log.error("%s is already dead!", player) + return + + # protected? + if target.protected: + log.debug("%s was protected and survives.", player) + return + + # in love? + if player in self.lovers: + log.debug("%s is in love! Killing them and their lover.", player) + for p in self.lovers: + # kill them and their lover + log.debug("Killed %s", p) + self.dead_this_night.append(p) + self.players[p].alive = False + return + + # else just kill them + log.info("Killed %s" % player) + self.dead_this_night.append(player) + target.alive = False + + def revive(self, player: str) -> None: + """Revive a player.""" + log.info("Players that will die this night are: %s", self.dead_this_night) + if player not in self.players: + log.error("Tried to revive unknown player: %s", player) + return + p = self.players[player] + if not p.alive: + p.alive = True + log.info("%s has been revived.", player) + else: + log.info("%s is already alive.", player) + + # ------------------------- + # helpers + # ------------------------- + def status(self) -> None: + """Log current players' statuses for debugging.""" + + # player values + for p in self.players.values(): + log.debug("%s -> role: %s, alive: %s, Protected: %s", + p.name, p.role, p.alive, p.protected) + # lovers + log.debug(f"Lovers: {self.lovers}") + + def role(func): + """Decorator for roles""" + def wrapper(self, *args, **kwargs): + # the role is the name of the function thats decorated + role = func.__name__.capitalize() + os.system('clear') + if role in self.get_remaining_roles(): + self.play_sound("roles", role) + self.play_sound("prompts", "wakes") + func(self, *args, **kwargs) + self.play_sound("roles", role) + self.play_sound("prompts", "sleep_again") + log.INFO("%s goes back to sleep." % role) + self.status() + + return wrapper + + # ------------------------- + # roles + # ------------------------- + + @role + def cupidon(self) -> None: + """Interactively pick two players to link by love.""" + self.play_sound("prompts", "linked_death") + self.play_sound("prompts", "first_bound") + p1 = self.select_someone() + if p1 is None: + return + self.play_sound("prompts", "second_bound") + p2 = self.select_someone() + if p2 is None: + return + if p1 == p2: + log.info("Cannot link a player to themselves.") + return + self.put_in_love(p1, p2) + + @role + def savior(self) -> None: + """Interactively choose someone to protect.""" + self.play_sound("prompts", "choose_protect") + protected = self.select_someone() + if protected is None: + return + self.players[protected].protected = True + log.debug("Protected: %s", protected) + + @role + def witch(self) -> None: + """Interactively choose to kill or revive someone""" + self.play_sound("prompts", "potions_choice") + self.play_sound("prompts", "might_die") + for player in self.dead_this_night: + self.show_text(self.players[player].name) + if len(self.used_potions) >= 2: + self.show_text("Vous avez utilisé toutes vos potions. ") + time.sleep(5) + else: + while True: + options = ("Death", "Revive", "Nothing") + potionchoice = self.choose_between(options).capitalize() + if potionchoice == "Revive" and "Revive" not in self.used_potions: + player = self.select_someone() + if player in self.dead_this_night: + self.used_potions.append("Revive") + self.revive(player) + return + elif player not in self.dead_this_night and not self.players[player].alive: + self.show_text("Tu ne peux pas faire revivre cette personne.") + elif self.players[player].alive: + self.show_text("Cette personne n'est pas morte.") + else: + log.info("Unknown error: Invalid player choice") + return + elif potionchoice == "Death" and "Death" not in self.used_potions: + player = self.select_someone() + if self.players[player].alive: + self.used_potions.append("Death") + self.kill(player) + time.sleep(1) + return + elif not self.players[player].alive: + self.show_text("Ce joueur est déjà mort.") + else: + log.info("Unknown error: Invalid player choice") + return + elif potionchoice == "Nothing": + self.show_text("Vous ne faites rien cette nuit.") + return + elif potionchoice in self.used_potions: + log.info("You already used this potion.") + else: + log.critical("Unknown error: Invalid potion choice.") + return + @role + def werewolf(self) -> None: + self.play_sound("prompts", "werewolf_choice") + player = self.select_someone() + self.kill(player) + + @role + def seer(self) -> None: + log.info("Choose a player to discover their role.") + while True: + player = self.select_someone() + if "Seer" == self.players[player].role: + self.show_text("You can't see your own role.") + else: + self.show_text(self.players[player].role) + time.sleep(3) + return + + @role + def hunter(self) -> None: + self.play_sound("prompts", "hunter") + player = self.select_someone() + self.kill(player) + + # ------------------------- + # game flow + # ------------------------- + def first_night_cycle(self) -> None: + self.play_sound("prompts", "fall_asleep_all") + self.cupidon() + self.couple() + self.savior() + self.werewolf() + self.witch() + self.seer() + self.day() + + def night(self) -> None: + self.dead_this_night = [] + self.play_sound("prompts", "fall_asleep_all") + self.savior() + self.werewolf() + self.witch() + self.seer() + self.day() + + def day(self) -> None: + self.play_sound("prompts", "wake_up_all") + self.play_sound("prompts", "night_deaths") + for player in self.dead_this_night: + self.play_sound("players", self.number_2_name(player)) + for player in self.dead_this_night: + if self.players[player].role == "Hunter": + self.hunter() + self.play_sound("prompts", "village_vote") + while True: + if count_fingers(stable_duration=4) == 6: + break + self.play_sound("prompts", "now_show_number") + player = self.select_someone() + self.kill(player) + self.night() + + + +if __name__ == "__main__": + print("---\nWerewolfGame\n---") + + # instantiate the game + game = Game() + + # I/O: read files and assign roles + loaded = game.load_lists() + try: + if loaded: + # start the game! + game.give_roles_to_players(loaded["players"], loaded["roles"], loaded["alias"]) + game.first_night_cycle() + + # CTRL+C + except KeyboardInterrupt: + print() # new line + log.info("Bye bye!") + sys.exit(0) + + # Any unhandled exception + except Exception as e: + log.exception("Unhandled exception: %s" % e) + sys.exit(1) + diff --git a/werewoolf/players.txt b/werewoolf/players.txt new file mode 100644 index 0000000..f11ae93 --- /dev/null +++ b/werewoolf/players.txt @@ -0,0 +1,6 @@ +Anavoi +Jean +Paul +Christophe +Jeanne +Julien diff --git a/werewoolf/randoms_words.txt b/werewoolf/randoms_words.txt new file mode 100644 index 0000000..7ce4242 --- /dev/null +++ b/werewoolf/randoms_words.txt @@ -0,0 +1,11 @@ +Abricot +Pomme +Fraise +Melon +Orange +Framboise +Cerise +Citron +Poire +Raisin +Prune \ No newline at end of file diff --git a/werewoolf/requirements.txt b/werewoolf/requirements.txt new file mode 100644 index 0000000..c638c1a --- /dev/null +++ b/werewoolf/requirements.txt @@ -0,0 +1,7 @@ +fuzzywuzzy==0.18.0 +opencv_python +mediapipe +piper-tts +playsound +art +pygobject \ No newline at end of file diff --git a/werewoolf/roles.txt b/werewoolf/roles.txt new file mode 100644 index 0000000..fcc099d --- /dev/null +++ b/werewoolf/roles.txt @@ -0,0 +1,10 @@ +Werewolf +Seer +Witch +Grandchild +Hunter +Werewolf +Werewolf +Villager +Cupidon +Villager \ No newline at end of file diff --git a/werewoolf/translation.py b/werewoolf/translation.py new file mode 100644 index 0000000..d94f467 --- /dev/null +++ b/werewoolf/translation.py @@ -0,0 +1,10 @@ +FR = { + "Seer": "Voyante", + "Werewolf": "Loups-Garous", + "Witch": "Sorciere", + "Cupidon": "Cupidon", + "Savior": "Salvateur", + "Villager": "Villageois", + "Grandchild": "Petite-Fille", + "Hunter": "Chasseur" +} \ No newline at end of file diff --git a/werewoolf/voices/fr/fr_FR-tom-medium.onnx b/werewoolf/voices/fr/fr_FR-tom-medium.onnx new file mode 100644 index 0000000..880e3e8 Binary files /dev/null and b/werewoolf/voices/fr/fr_FR-tom-medium.onnx differ diff --git a/werewoolf/voices/fr/fr_FR-tom-medium.onnx.json b/werewoolf/voices/fr/fr_FR-tom-medium.onnx.json new file mode 100644 index 0000000..526f2bd --- /dev/null +++ b/werewoolf/voices/fr/fr_FR-tom-medium.onnx.json @@ -0,0 +1,502 @@ +{ + "dataset": "tom", + "audio": { + "sample_rate": 44100, + "quality": "medium" + }, + "espeak": { + "voice": "fr" + }, + "language": { + "code": "fr_FR", + "family": "fr", + "region": "FR", + "name_native": "Français", + "name_english": "French", + "country_english": "France" + }, + "inference": { + "noise_scale": 0.667, + "length_scale": 1, + "noise_w": 0.8 + }, + "phoneme_type": "espeak", + "phoneme_map": {}, + "phoneme_id_map": { + " ": [ + 3 + ], + "!": [ + 4 + ], + "\"": [ + 150 + ], + "#": [ + 149 + ], + "$": [ + 2 + ], + "'": [ + 5 + ], + "(": [ + 6 + ], + ")": [ + 7 + ], + ",": [ + 8 + ], + "-": [ + 9 + ], + ".": [ + 10 + ], + "0": [ + 130 + ], + "1": [ + 131 + ], + "2": [ + 132 + ], + "3": [ + 133 + ], + "4": [ + 134 + ], + "5": [ + 135 + ], + "6": [ + 136 + ], + "7": [ + 137 + ], + "8": [ + 138 + ], + "9": [ + 139 + ], + ":": [ + 11 + ], + ";": [ + 12 + ], + "?": [ + 13 + ], + "X": [ + 156 + ], + "^": [ + 1 + ], + "_": [ + 0 + ], + "a": [ + 14 + ], + "b": [ + 15 + ], + "c": [ + 16 + ], + "d": [ + 17 + ], + "e": [ + 18 + ], + "f": [ + 19 + ], + "g": [ + 154 + ], + "h": [ + 20 + ], + "i": [ + 21 + ], + "j": [ + 22 + ], + "k": [ + 23 + ], + "l": [ + 24 + ], + "m": [ + 25 + ], + "n": [ + 26 + ], + "o": [ + 27 + ], + "p": [ + 28 + ], + "q": [ + 29 + ], + "r": [ + 30 + ], + "s": [ + 31 + ], + "t": [ + 32 + ], + "u": [ + 33 + ], + "v": [ + 34 + ], + "w": [ + 35 + ], + "x": [ + 36 + ], + "y": [ + 37 + ], + "z": [ + 38 + ], + "æ": [ + 39 + ], + "ç": [ + 40 + ], + "ð": [ + 41 + ], + "ø": [ + 42 + ], + "ħ": [ + 43 + ], + "ŋ": [ + 44 + ], + "œ": [ + 45 + ], + "ǀ": [ + 46 + ], + "ǁ": [ + 47 + ], + "ǂ": [ + 48 + ], + "ǃ": [ + 49 + ], + "ɐ": [ + 50 + ], + "ɑ": [ + 51 + ], + "ɒ": [ + 52 + ], + "ɓ": [ + 53 + ], + "ɔ": [ + 54 + ], + "ɕ": [ + 55 + ], + "ɖ": [ + 56 + ], + "ɗ": [ + 57 + ], + "ɘ": [ + 58 + ], + "ə": [ + 59 + ], + "ɚ": [ + 60 + ], + "ɛ": [ + 61 + ], + "ɜ": [ + 62 + ], + "ɞ": [ + 63 + ], + "ɟ": [ + 64 + ], + "ɠ": [ + 65 + ], + "ɡ": [ + 66 + ], + "ɢ": [ + 67 + ], + "ɣ": [ + 68 + ], + "ɤ": [ + 69 + ], + "ɥ": [ + 70 + ], + "ɦ": [ + 71 + ], + "ɧ": [ + 72 + ], + "ɨ": [ + 73 + ], + "ɪ": [ + 74 + ], + "ɫ": [ + 75 + ], + "ɬ": [ + 76 + ], + "ɭ": [ + 77 + ], + "ɮ": [ + 78 + ], + "ɯ": [ + 79 + ], + "ɰ": [ + 80 + ], + "ɱ": [ + 81 + ], + "ɲ": [ + 82 + ], + "ɳ": [ + 83 + ], + "ɴ": [ + 84 + ], + "ɵ": [ + 85 + ], + "ɶ": [ + 86 + ], + "ɸ": [ + 87 + ], + "ɹ": [ + 88 + ], + "ɺ": [ + 89 + ], + "ɻ": [ + 90 + ], + "ɽ": [ + 91 + ], + "ɾ": [ + 92 + ], + "ʀ": [ + 93 + ], + "ʁ": [ + 94 + ], + "ʂ": [ + 95 + ], + "ʃ": [ + 96 + ], + "ʄ": [ + 97 + ], + "ʈ": [ + 98 + ], + "ʉ": [ + 99 + ], + "ʊ": [ + 100 + ], + "ʋ": [ + 101 + ], + "ʌ": [ + 102 + ], + "ʍ": [ + 103 + ], + "ʎ": [ + 104 + ], + "ʏ": [ + 105 + ], + "ʐ": [ + 106 + ], + "ʑ": [ + 107 + ], + "ʒ": [ + 108 + ], + "ʔ": [ + 109 + ], + "ʕ": [ + 110 + ], + "ʘ": [ + 111 + ], + "ʙ": [ + 112 + ], + "ʛ": [ + 113 + ], + "ʜ": [ + 114 + ], + "ʝ": [ + 115 + ], + "ʟ": [ + 116 + ], + "ʡ": [ + 117 + ], + "ʢ": [ + 118 + ], + "ʦ": [ + 155 + ], + "ʰ": [ + 145 + ], + "ʲ": [ + 119 + ], + "ˈ": [ + 120 + ], + "ˌ": [ + 121 + ], + "ː": [ + 122 + ], + "ˑ": [ + 123 + ], + "˞": [ + 124 + ], + "ˤ": [ + 146 + ], + "̃": [ + 141 + ], + "̧": [ + 140 + ], + "̩": [ + 144 + ], + "̪": [ + 142 + ], + "̯": [ + 143 + ], + "̺": [ + 152 + ], + "̻": [ + 153 + ], + "β": [ + 125 + ], + "ε": [ + 147 + ], + "θ": [ + 126 + ], + "χ": [ + 127 + ], + "ᵻ": [ + 128 + ], + "↑": [ + 151 + ], + "↓": [ + 148 + ], + "ⱱ": [ + 129 + ] + }, + "num_symbols": 256, + "num_speakers": 1, + "speaker_id_map": {}, + "piper_version": "1.0.0" +} diff --git a/werewoolf/voicesline/prompts/en/bound.wav b/werewoolf/voicesline/prompts/en/bound.wav new file mode 100644 index 0000000..a8f3134 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/bound.wav differ diff --git a/werewoolf/voicesline/prompts/en/choose_protect.wav b/werewoolf/voicesline/prompts/en/choose_protect.wav new file mode 100644 index 0000000..fc97449 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/choose_protect.wav differ diff --git a/werewoolf/voicesline/prompts/en/fall_asleep_all.wav b/werewoolf/voicesline/prompts/en/fall_asleep_all.wav new file mode 100644 index 0000000..56d2905 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/fall_asleep_all.wav differ diff --git a/werewoolf/voicesline/prompts/en/first_bound.wav b/werewoolf/voicesline/prompts/en/first_bound.wav new file mode 100644 index 0000000..bc697cf Binary files /dev/null and b/werewoolf/voicesline/prompts/en/first_bound.wav differ diff --git a/werewoolf/voicesline/prompts/en/hunter.wav b/werewoolf/voicesline/prompts/en/hunter.wav new file mode 100644 index 0000000..80bb54c Binary files /dev/null and b/werewoolf/voicesline/prompts/en/hunter.wav differ diff --git a/werewoolf/voicesline/prompts/en/linked_death.wav b/werewoolf/voicesline/prompts/en/linked_death.wav new file mode 100644 index 0000000..d50a0ed Binary files /dev/null and b/werewoolf/voicesline/prompts/en/linked_death.wav differ diff --git a/werewoolf/voicesline/prompts/en/lovers.wav b/werewoolf/voicesline/prompts/en/lovers.wav new file mode 100644 index 0000000..2d1e3d1 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/lovers.wav differ diff --git a/werewoolf/voicesline/prompts/en/might_die.wav b/werewoolf/voicesline/prompts/en/might_die.wav new file mode 100644 index 0000000..a0d9c1f Binary files /dev/null and b/werewoolf/voicesline/prompts/en/might_die.wav differ diff --git a/werewoolf/voicesline/prompts/en/night_deaths.wav b/werewoolf/voicesline/prompts/en/night_deaths.wav new file mode 100644 index 0000000..57b0028 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/night_deaths.wav differ diff --git a/werewoolf/voicesline/prompts/en/now_show_number.wav b/werewoolf/voicesline/prompts/en/now_show_number.wav new file mode 100644 index 0000000..9b66c8d Binary files /dev/null and b/werewoolf/voicesline/prompts/en/now_show_number.wav differ diff --git a/werewoolf/voicesline/prompts/en/potions_choice.wav b/werewoolf/voicesline/prompts/en/potions_choice.wav new file mode 100644 index 0000000..571acf2 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/potions_choice.wav differ diff --git a/werewoolf/voicesline/prompts/en/reveal.wav b/werewoolf/voicesline/prompts/en/reveal.wav new file mode 100644 index 0000000..fcb8b25 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/reveal.wav differ diff --git a/werewoolf/voicesline/prompts/en/second_bound.wav b/werewoolf/voicesline/prompts/en/second_bound.wav new file mode 100644 index 0000000..782ed53 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/second_bound.wav differ diff --git a/werewoolf/voicesline/prompts/en/sleep.wav b/werewoolf/voicesline/prompts/en/sleep.wav new file mode 100644 index 0000000..c4d2032 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/sleep.wav differ diff --git a/werewoolf/voicesline/prompts/en/sleep_again.wav b/werewoolf/voicesline/prompts/en/sleep_again.wav new file mode 100644 index 0000000..24696af Binary files /dev/null and b/werewoolf/voicesline/prompts/en/sleep_again.wav differ diff --git a/werewoolf/voicesline/prompts/en/village_vote.wav b/werewoolf/voicesline/prompts/en/village_vote.wav new file mode 100644 index 0000000..821695f Binary files /dev/null and b/werewoolf/voicesline/prompts/en/village_vote.wav differ diff --git a/werewoolf/voicesline/prompts/en/wake_up_all.wav b/werewoolf/voicesline/prompts/en/wake_up_all.wav new file mode 100644 index 0000000..4eb3caa Binary files /dev/null and b/werewoolf/voicesline/prompts/en/wake_up_all.wav differ diff --git a/werewoolf/voicesline/prompts/en/wakes.wav b/werewoolf/voicesline/prompts/en/wakes.wav new file mode 100644 index 0000000..e8f5aa3 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/wakes.wav differ diff --git a/werewoolf/voicesline/prompts/en/werewolf_choice.wav b/werewoolf/voicesline/prompts/en/werewolf_choice.wav new file mode 100644 index 0000000..7beba37 Binary files /dev/null and b/werewoolf/voicesline/prompts/en/werewolf_choice.wav differ diff --git a/werewoolf/voicesline/prompts/fr/bound.wav b/werewoolf/voicesline/prompts/fr/bound.wav new file mode 100644 index 0000000..0957a70 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/bound.wav differ diff --git a/werewoolf/voicesline/prompts/fr/cheater.wav b/werewoolf/voicesline/prompts/fr/cheater.wav new file mode 100644 index 0000000..334dfdb Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/cheater.wav differ diff --git a/werewoolf/voicesline/prompts/fr/choose_protect.wav b/werewoolf/voicesline/prompts/fr/choose_protect.wav new file mode 100644 index 0000000..6b006fa Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/choose_protect.wav differ diff --git a/werewoolf/voicesline/prompts/fr/fall_asleep_all.wav b/werewoolf/voicesline/prompts/fr/fall_asleep_all.wav new file mode 100644 index 0000000..8d65031 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/fall_asleep_all.wav differ diff --git a/werewoolf/voicesline/prompts/fr/first_bound.wav b/werewoolf/voicesline/prompts/fr/first_bound.wav new file mode 100644 index 0000000..31b3c10 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/first_bound.wav differ diff --git a/werewoolf/voicesline/prompts/fr/first_wakes.wav b/werewoolf/voicesline/prompts/fr/first_wakes.wav new file mode 100644 index 0000000..e49ce3b Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/first_wakes.wav differ diff --git a/werewoolf/voicesline/prompts/fr/hunter.wav b/werewoolf/voicesline/prompts/fr/hunter.wav new file mode 100644 index 0000000..3188118 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/hunter.wav differ diff --git a/werewoolf/voicesline/prompts/fr/linked_death.wav b/werewoolf/voicesline/prompts/fr/linked_death.wav new file mode 100644 index 0000000..028e07f Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/linked_death.wav differ diff --git a/werewoolf/voicesline/prompts/fr/lovers.wav b/werewoolf/voicesline/prompts/fr/lovers.wav new file mode 100644 index 0000000..e5cc0f6 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/lovers.wav differ diff --git a/werewoolf/voicesline/prompts/fr/might_die.wav b/werewoolf/voicesline/prompts/fr/might_die.wav new file mode 100644 index 0000000..022b8d8 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/might_die.wav differ diff --git a/werewoolf/voicesline/prompts/fr/night_deaths.wav b/werewoolf/voicesline/prompts/fr/night_deaths.wav new file mode 100644 index 0000000..5800794 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/night_deaths.wav differ diff --git a/werewoolf/voicesline/prompts/fr/now_show_number.wav b/werewoolf/voicesline/prompts/fr/now_show_number.wav new file mode 100644 index 0000000..cd97fe1 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/now_show_number.wav differ diff --git a/werewoolf/voicesline/prompts/fr/potions_choice.wav b/werewoolf/voicesline/prompts/fr/potions_choice.wav new file mode 100644 index 0000000..aaeba83 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/potions_choice.wav differ diff --git a/werewoolf/voicesline/prompts/fr/reveal.wav b/werewoolf/voicesline/prompts/fr/reveal.wav new file mode 100644 index 0000000..a9345f7 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/reveal.wav differ diff --git a/werewoolf/voicesline/prompts/fr/second_bound.wav b/werewoolf/voicesline/prompts/fr/second_bound.wav new file mode 100644 index 0000000..781eca7 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/second_bound.wav differ diff --git a/werewoolf/voicesline/prompts/fr/sleep.wav b/werewoolf/voicesline/prompts/fr/sleep.wav new file mode 100644 index 0000000..6e31abe Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/sleep.wav differ diff --git a/werewoolf/voicesline/prompts/fr/sleep_again.wav b/werewoolf/voicesline/prompts/fr/sleep_again.wav new file mode 100644 index 0000000..b3da3ac Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/sleep_again.wav differ diff --git a/werewoolf/voicesline/prompts/fr/village_vote.wav b/werewoolf/voicesline/prompts/fr/village_vote.wav new file mode 100644 index 0000000..875fa71 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/village_vote.wav differ diff --git a/werewoolf/voicesline/prompts/fr/wake_up_all.wav b/werewoolf/voicesline/prompts/fr/wake_up_all.wav new file mode 100644 index 0000000..b3b5504 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/wake_up_all.wav differ diff --git a/werewoolf/voicesline/prompts/fr/wakes.wav b/werewoolf/voicesline/prompts/fr/wakes.wav new file mode 100644 index 0000000..92bd902 Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/wakes.wav differ diff --git a/werewoolf/voicesline/prompts/fr/werewolf_choice.wav b/werewoolf/voicesline/prompts/fr/werewolf_choice.wav new file mode 100644 index 0000000..d0d460a Binary files /dev/null and b/werewoolf/voicesline/prompts/fr/werewolf_choice.wav differ diff --git a/werewoolf/voicesline/roles/en/Cupidon.wav b/werewoolf/voicesline/roles/en/Cupidon.wav new file mode 100644 index 0000000..1c9b15c Binary files /dev/null and b/werewoolf/voicesline/roles/en/Cupidon.wav differ diff --git a/werewoolf/voicesline/roles/en/Hunter.wav b/werewoolf/voicesline/roles/en/Hunter.wav new file mode 100644 index 0000000..0bd6d49 Binary files /dev/null and b/werewoolf/voicesline/roles/en/Hunter.wav differ diff --git a/werewoolf/voicesline/roles/en/Savior.wav b/werewoolf/voicesline/roles/en/Savior.wav new file mode 100644 index 0000000..577c354 Binary files /dev/null and b/werewoolf/voicesline/roles/en/Savior.wav differ diff --git a/werewoolf/voicesline/roles/en/Seer.wav b/werewoolf/voicesline/roles/en/Seer.wav new file mode 100644 index 0000000..150f33a Binary files /dev/null and b/werewoolf/voicesline/roles/en/Seer.wav differ diff --git a/werewoolf/voicesline/roles/en/Werewolf.wav b/werewoolf/voicesline/roles/en/Werewolf.wav new file mode 100644 index 0000000..1b08b7f Binary files /dev/null and b/werewoolf/voicesline/roles/en/Werewolf.wav differ diff --git a/werewoolf/voicesline/roles/en/Witch.wav b/werewoolf/voicesline/roles/en/Witch.wav new file mode 100644 index 0000000..e980847 Binary files /dev/null and b/werewoolf/voicesline/roles/en/Witch.wav differ diff --git a/werewoolf/voicesline/roles/fr/Cupidon.wav b/werewoolf/voicesline/roles/fr/Cupidon.wav new file mode 100644 index 0000000..37dfd77 Binary files /dev/null and b/werewoolf/voicesline/roles/fr/Cupidon.wav differ diff --git a/werewoolf/voicesline/roles/fr/Hunter.wav b/werewoolf/voicesline/roles/fr/Hunter.wav new file mode 100644 index 0000000..bc7d2b0 Binary files /dev/null and b/werewoolf/voicesline/roles/fr/Hunter.wav differ diff --git a/werewoolf/voicesline/roles/fr/Savior.wav b/werewoolf/voicesline/roles/fr/Savior.wav new file mode 100644 index 0000000..5715ef4 Binary files /dev/null and b/werewoolf/voicesline/roles/fr/Savior.wav differ diff --git a/werewoolf/voicesline/roles/fr/Seer.wav b/werewoolf/voicesline/roles/fr/Seer.wav new file mode 100644 index 0000000..1e0a07c Binary files /dev/null and b/werewoolf/voicesline/roles/fr/Seer.wav differ diff --git a/werewoolf/voicesline/roles/fr/Werewolf.wav b/werewoolf/voicesline/roles/fr/Werewolf.wav new file mode 100644 index 0000000..e098cf8 Binary files /dev/null and b/werewoolf/voicesline/roles/fr/Werewolf.wav differ diff --git a/werewoolf/voicesline/roles/fr/Witch.wav b/werewoolf/voicesline/roles/fr/Witch.wav new file mode 100644 index 0000000..4ac115f Binary files /dev/null and b/werewoolf/voicesline/roles/fr/Witch.wav differ