Switched to Linux and it changed the line encoding, probably, cause every file is showing as changed.

This commit is contained in:
csd4ni3l
2025-11-09 17:56:56 +01:00
parent d1b238b239
commit a4ec759599
22 changed files with 2414 additions and 2414 deletions

View File

@@ -1,118 +1,118 @@
name: Build and Release name: Build and Release
on: push on: push
jobs: jobs:
build: build:
name: Build on ${{ matrix.os }} name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
include: include:
- os: ubuntu-22.04 - os: ubuntu-22.04
platform: linux platform: linux
python-version: "3.11" python-version: "3.11"
- os: windows-latest - os: windows-latest
platform: windows platform: windows
python-version: "3.11" python-version: "3.11"
steps: steps:
- name: Check-out repository - name: Check-out repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: "3.11" python-version: "3.11"
architecture: "x64" architecture: "x64"
cache: "pip" cache: "pip"
cache-dependency-path: | cache-dependency-path: |
**/requirements*.txt **/requirements*.txt
- name: Install Dependencies - name: Install Dependencies
run: pip install -r requirements.txt run: pip install -r requirements.txt
- name: Build Executable - name: Build Executable
uses: Nuitka/Nuitka-Action@main uses: Nuitka/Nuitka-Action@main
with: with:
nuitka-version: main nuitka-version: main
script-name: run.py script-name: run.py
nofollow-import-to: "*tk*,_codecs,encodings,multiprocessing,gi" nofollow-import-to: "*tk*,_codecs,encodings,multiprocessing,gi"
disable-plugins: tk-inter,dill-compat,eventlet,gevent,pyqt5,pyqt6,pyside2,pyside6,delvewheel,pywebview,matplotlib,spacy,enum-compat,pbr-compat,gevent,pmw-freezer,transformers,upx,kivy,options-nanny,multiprocessing,gi disable-plugins: tk-inter,dill-compat,eventlet,gevent,pyqt5,pyqt6,pyside2,pyside6,delvewheel,pywebview,matplotlib,spacy,enum-compat,pbr-compat,gevent,pmw-freezer,transformers,upx,kivy,options-nanny,multiprocessing,gi
include-data-dir: assets=assets include-data-dir: assets=assets
include-data-files: CREDITS=CREDITS include-data-files: CREDITS=CREDITS
mode: onefile mode: onefile
output-file: ConnectTheCurrent output-file: ConnectTheCurrent
- name: Locate and rename executable (Linux) - name: Locate and rename executable (Linux)
if: matrix.os == 'ubuntu-22.04' if: matrix.os == 'ubuntu-22.04'
run: | run: |
set -euo pipefail set -euo pipefail
echo "Searching for built Linux binary..." echo "Searching for built Linux binary..."
# List to help debugging when paths change # List to help debugging when paths change
ls -laR . | head -n 500 || true ls -laR . | head -n 500 || true
BIN=$(find . -maxdepth 4 -type f -name 'ConnectTheCurrent*' -perm -u+x | head -n1 || true) BIN=$(find . -maxdepth 4 -type f -name 'ConnectTheCurrent*' -perm -u+x | head -n1 || true)
if [ -z "${BIN}" ]; then if [ -z "${BIN}" ]; then
echo "ERROR: No Linux binary found after build" echo "ERROR: No Linux binary found after build"
exit 1 exit 1
fi fi
echo "Found: ${BIN}" echo "Found: ${BIN}"
mkdir -p build_output mkdir -p build_output
cp "${BIN}" build_output/ConnectTheCurrent.bin cp "${BIN}" build_output/ConnectTheCurrent.bin
chmod +x build_output/ConnectTheCurrent.bin chmod +x build_output/ConnectTheCurrent.bin
echo "Executable ready: build_output/ConnectTheCurrent.bin" echo "Executable ready: build_output/ConnectTheCurrent.bin"
shell: bash shell: bash
- name: Locate and rename executable (Windows) - name: Locate and rename executable (Windows)
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: | run: |
Write-Host "Searching for built Windows binary..." Write-Host "Searching for built Windows binary..."
Get-ChildItem -Recurse -File -Filter 'ConnectTheCurrent*.exe' | Select-Object -First 1 | ForEach-Object { Get-ChildItem -Recurse -File -Filter 'ConnectTheCurrent*.exe' | Select-Object -First 1 | ForEach-Object {
Write-Host ("Found: " + $_.FullName) Write-Host ("Found: " + $_.FullName)
New-Item -ItemType Directory -Force -Path build_output | Out-Null New-Item -ItemType Directory -Force -Path build_output | Out-Null
Copy-Item $_.FullName "build_output\ConnectTheCurrent.exe" Copy-Item $_.FullName "build_output\ConnectTheCurrent.exe"
Write-Host "Executable ready: build_output\ConnectTheCurrent.exe" Write-Host "Executable ready: build_output\ConnectTheCurrent.exe"
} }
if (!(Test-Path build_output\ConnectTheCurrent.exe)) { if (!(Test-Path build_output\ConnectTheCurrent.exe)) {
Write-Error "ERROR: No Windows binary found after build" Write-Error "ERROR: No Windows binary found after build"
exit 1 exit 1
} }
shell: pwsh shell: pwsh
- name: Upload build artifact - name: Upload build artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.platform }} name: ${{ matrix.platform }}
path: build_output/ConnectTheCurrent.* path: build_output/ConnectTheCurrent.*
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build needs: build
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Download All Build Artifacts - name: Download All Build Artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
path: downloads path: downloads
- name: Create release (if missing) and upload artifacts to tag - name: Create release (if missing) and upload artifacts to tag
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
set -euo pipefail set -euo pipefail
TAG="latest" TAG="latest"
echo "Target release tag: $TAG" echo "Target release tag: $TAG"
if gh release view "$TAG" >/dev/null 2>&1; then if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists; will upload assets with --clobber" echo "Release $TAG already exists; will upload assets with --clobber"
else else
gh release create "$TAG" \ gh release create "$TAG" \
--title "$TAG" \ --title "$TAG" \
--notes "Automated build for $TAG" --notes "Automated build for $TAG"
fi fi
# Upload the executables directly (no zip files) # Upload the executables directly (no zip files)
gh release upload "$TAG" downloads/linux/ConnectTheCurrent.bin --clobber gh release upload "$TAG" downloads/linux/ConnectTheCurrent.bin --clobber
gh release upload "$TAG" downloads/windows/ConnectTheCurrent.exe --clobber gh release upload "$TAG" downloads/windows/ConnectTheCurrent.exe --clobber

364
.gitignore vendored
View File

@@ -1,182 +1,182 @@
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
# C extensions # C extensions
*.so *.so
# Distribution / packaging # Distribution / packaging
.Python .Python
build/ build/
develop-eggs/ develop-eggs/
dist/ dist/
downloads/ downloads/
eggs/ eggs/
.eggs/ .eggs/
lib/ lib/
lib64/ lib64/
parts/ parts/
sdist/ sdist/
var/ var/
wheels/ wheels/
share/python-wheels/ share/python-wheels/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
MANIFEST MANIFEST
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # 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. # before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest *.manifest
*.spec *.spec
# Installer logs # Installer logs
pip-log.txt pip-log.txt
pip-delete-this-directory.txt pip-delete-this-directory.txt
# Unit test / coverage reports # Unit test / coverage reports
htmlcov/ htmlcov/
.tox/ .tox/
.nox/ .nox/
.coverage .coverage
.coverage.* .coverage.*
.cache .cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*.cover *.cover
*.py,cover *.py,cover
.hypothesis/ .hypothesis/
.pytest_cache/ .pytest_cache/
cover/ cover/
# Translations # Translations
*.mo *.mo
*.pot *.pot
# Django stuff: # Django stuff:
*.log *.log
local_settings.py local_settings.py
db.sqlite3 db.sqlite3
db.sqlite3-journal db.sqlite3-journal
# Flask stuff: # Flask stuff:
instance/ instance/
.webassets-cache .webassets-cache
# Scrapy stuff: # Scrapy stuff:
.scrapy .scrapy
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
# PyBuilder # PyBuilder
.pybuilder/ .pybuilder/
target/ target/
# Jupyter Notebook # Jupyter Notebook
.ipynb_checkpoints .ipynb_checkpoints
# IPython # IPython
profile_default/ profile_default/
ipython_config.py ipython_config.py
# pyenv # pyenv
# For a library or package, you might want to ignore these files since the code is # 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: # intended to run in multiple environments; otherwise, check them in:
# .python-version # .python-version
# pipenv # pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # 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 # 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 # having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies. # install all needed dependencies.
#Pipfile.lock #Pipfile.lock
# UV # UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. # 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 # This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries. # commonly ignored for libraries.
#uv.lock #uv.lock
# poetry # poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # 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 # This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries. # commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock #poetry.lock
# pdm # pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock #pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control. # in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml .pdm.toml
.pdm-python .pdm-python
.pdm-build/ .pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/ __pypackages__/
# Celery stuff # Celery stuff
celerybeat-schedule celerybeat-schedule
celerybeat.pid celerybeat.pid
# SageMath parsed files # SageMath parsed files
*.sage.py *.sage.py
# Environments # Environments
.env .env
.venv .venv
env/ env/
venv/ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
.spyproject .spyproject
# Rope project settings # Rope project settings
.ropeproject .ropeproject
# mkdocs documentation # mkdocs documentation
/site /site
# mypy # mypy
.mypy_cache/ .mypy_cache/
.dmypy.json .dmypy.json
dmypy.json dmypy.json
# Pyre type checker # Pyre type checker
.pyre/ .pyre/
# pytype static type analyzer # pytype static type analyzer
.pytype/ .pytype/
# Cython debug symbols # Cython debug symbols
cython_debug/ cython_debug/
# PyCharm # PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can # 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 # 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 # 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. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
# Ruff stuff: # Ruff stuff:
.ruff_cache/ .ruff_cache/
# PyPI configuration file # PyPI configuration file
.pypirc .pypirc
.vscode .vscode
test*.py test*.py
.zed/ .zed/
logs/ logs/
logs logs
settings.json settings.json

View File

@@ -1 +1 @@
3.11 3.11

28
CREDITS
View File

@@ -1,14 +1,14 @@
Sound Effect by freesound_community from Pixabay (cut to the important part) Sound Effect by freesound_community from Pixabay (cut to the important part)
https://pixabay.com/sound-effects/cutting-clipping-wire-copper-80373/ https://pixabay.com/sound-effects/cutting-clipping-wire-copper-80373/
The Roboto Black font used in this project is licensed under the Open Font License. Read assets/fonts/OFL.txt for more information. The Roboto Black font used in this project is licensed under the Open Font License. Read assets/fonts/OFL.txt for more information.
Huge Thanks to Python for being the programming language used in this game. Huge Thanks to Python for being the programming language used in this game.
https://www.python.org/ https://www.python.org/
Huge thanks to Arcade and Pyglet for being the graphical engines used in this game. Huge thanks to Arcade and Pyglet for being the graphical engines used in this game.
https://arcade.academy/ https://arcade.academy/
https://pyglet.readthedocs.io/en/latest/ https://pyglet.readthedocs.io/en/latest/
Thanks to the following other libraries used in this game: Thanks to the following other libraries used in this game:
pypresence - https://github.com/qwertyquerty/pypresence - Used for Discord Rich Presence pypresence - https://github.com/qwertyquerty/pypresence - Used for Discord Rich Presence

1348
LICENSE

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
CTC: Connect The Current is a game where you have a power source, and you have to direct the power lines to houses by rotating them into the correct direction. CTC: Connect The Current is a game where you have a power source, and you have to direct the power lines to houses by rotating them into the correct direction.
Tutorial: Tutorial:
In Connect the Current, you have to rotate power lines so power reaches to all of the houses. In Connect the Current, you have to rotate power lines so power reaches to all of the houses.
- Every line has to be connected on all of it's sides. - Every line has to be connected on all of it's sides.
- When needed, you might have to create loops of power or branches with no house linked to them. - When needed, you might have to create loops of power or branches with no house linked to them.
(This is also because it's randomly generated and i couldn't find a way to generate maps with no meaningless branches) (This is also because it's randomly generated and i couldn't find a way to generate maps with no meaningless branches)
- To rotate a line, just click on it and it will change its rotation. - To rotate a line, just click on it and it will change its rotation.
- Maps are randomly generated, difficulty(size, source count, house count) depends on what you pick and grows exponentially. - Maps are randomly generated, difficulty(size, source count, house count) depends on what you pick and grows exponentially.

View File

@@ -1,60 +1,60 @@
import arcade, arcade.gui import arcade, arcade.gui
from utils.constants import ROTATIONS, NEIGHBOURS from utils.constants import ROTATIONS, NEIGHBOURS
from utils.preload import TEXTURE_MAP, wire_sound_effect from utils.preload import TEXTURE_MAP, wire_sound_effect
def get_opposite(direction): def get_opposite(direction):
if direction == "l": if direction == "l":
return "r" return "r"
elif direction == "r": elif direction == "r":
return "l" return "l"
elif direction == "t": elif direction == "t":
return "b" return "b"
elif direction == "b": elif direction == "b":
return "t" return "t"
class Cell(arcade.Sprite): class Cell(arcade.Sprite):
def __init__(self, cell_type, x, y, left_neighbour, top_neighbour): def __init__(self, cell_type, x, y, left_neighbour, top_neighbour):
super().__init__(TEXTURE_MAP[cell_type, ROTATIONS[cell_type][0] if cell_type in ROTATIONS else "cross", cell_type == "power_source"], center_x=x, center_y=y) super().__init__(TEXTURE_MAP[cell_type, ROTATIONS[cell_type][0] if cell_type in ROTATIONS else "cross", cell_type == "power_source"], center_x=x, center_y=y)
self.rotation = ROTATIONS[cell_type][0] if cell_type in ROTATIONS else "cross" self.rotation = ROTATIONS[cell_type][0] if cell_type in ROTATIONS else "cross"
self.cell_type = cell_type self.cell_type = cell_type
self.powered = False self.powered = False
self.left_neighbour, self.top_neighbour = left_neighbour, top_neighbour self.left_neighbour, self.top_neighbour = left_neighbour, top_neighbour
self.right_neighbour, self.bottom_neighbour = None, None self.right_neighbour, self.bottom_neighbour = None, None
def get_neighbour(self, name): def get_neighbour(self, name):
if name == "l": if name == "l":
return self.left_neighbour return self.left_neighbour
elif name == "r": elif name == "r":
return self.right_neighbour return self.right_neighbour
elif name == "b": elif name == "b":
return self.bottom_neighbour return self.bottom_neighbour
elif name == "t": elif name == "t":
return self.top_neighbour return self.top_neighbour
def get_connected_neighbours(self, include_houses=False): def get_connected_neighbours(self, include_houses=False):
return [ return [
self.get_neighbour(neighbour_direction) for neighbour_direction in NEIGHBOURS[self.rotation] self.get_neighbour(neighbour_direction) for neighbour_direction in NEIGHBOURS[self.rotation]
if ( if (
self.get_neighbour(neighbour_direction) and self.get_neighbour(neighbour_direction) and
(include_houses or self.get_neighbour(neighbour_direction).cell_type != "house") and (include_houses or self.get_neighbour(neighbour_direction).cell_type != "house") and
get_opposite(neighbour_direction) in NEIGHBOURS[self.get_neighbour(neighbour_direction).rotation] get_opposite(neighbour_direction) in NEIGHBOURS[self.get_neighbour(neighbour_direction).rotation]
) )
] ]
def update_visual(self): def update_visual(self):
self.texture = TEXTURE_MAP[(self.cell_type, self.rotation, self.powered)] self.texture = TEXTURE_MAP[(self.cell_type, self.rotation, self.powered)]
def next_rotation(self, sfx, sfx_volume): def next_rotation(self, sfx, sfx_volume):
if sfx: if sfx:
wire_sound_effect.play(volume=sfx_volume / 50) wire_sound_effect.play(volume=sfx_volume / 50)
current_index = ROTATIONS[self.cell_type].index(self.rotation) current_index = ROTATIONS[self.cell_type].index(self.rotation)
if current_index + 1 == len(ROTATIONS[self.cell_type]): if current_index + 1 == len(ROTATIONS[self.cell_type]):
self.rotation = ROTATIONS[self.cell_type][0] self.rotation = ROTATIONS[self.cell_type][0]
else: else:
self.rotation = ROTATIONS[self.cell_type][current_index + 1] self.rotation = ROTATIONS[self.cell_type][current_index + 1]
self.update_visual() self.update_visual()

View File

@@ -1,17 +1,17 @@
from game.cell import Cell from game.cell import Cell
class House(Cell): class House(Cell):
def __init__(self, x, y, left_neighbour, top_neighbour): def __init__(self, x, y, left_neighbour, top_neighbour):
super().__init__("house", x, y, left_neighbour, top_neighbour) super().__init__("house", x, y, left_neighbour, top_neighbour)
class PowerSource(Cell): class PowerSource(Cell):
def __init__(self, x, y, left_neighbour, top_neighbour): def __init__(self, x, y, left_neighbour, top_neighbour):
super().__init__("power_source", x, y, left_neighbour, top_neighbour) super().__init__("power_source", x, y, left_neighbour, top_neighbour)
self.on_click = lambda e: self.next_rotation() self.on_click = lambda e: self.next_rotation()
class PowerLine(Cell): class PowerLine(Cell):
def __init__(self, cell_type, x, y, left_neighbour, top_neighbour): def __init__(self, cell_type, x, y, left_neighbour, top_neighbour):
super().__init__(cell_type, x, y, left_neighbour, top_neighbour) super().__init__(cell_type, x, y, left_neighbour, top_neighbour)
if not cell_type == "cross": if not cell_type == "cross":
self.on_click = lambda e: self.next_rotation() self.on_click = lambda e: self.next_rotation()

View File

@@ -1,130 +1,130 @@
import random import random
from utils.constants import ROTATIONS, NEIGHBOURS, DIRECTIONS from utils.constants import ROTATIONS, NEIGHBOURS, DIRECTIONS
def in_bounds(x, y, size): def in_bounds(x, y, size):
return 0 <= x < size and 0 <= y < size return 0 <= x < size and 0 <= y < size
def classify_tile(conns): def classify_tile(conns):
for rotation, connections in NEIGHBOURS.items(): for rotation, connections in NEIGHBOURS.items():
if conns == connections: if conns == connections:
for cell_type, rotations in ROTATIONS.items(): for cell_type, rotations in ROTATIONS.items():
if rotation in rotations: if rotation in rotations:
return cell_type return cell_type
print(f"Unknown: {conns}") print(f"Unknown: {conns}")
return "cross" return "cross"
def add_cycles(conns, num_cycles): def add_cycles(conns, num_cycles):
size = len(conns) size = len(conns)
added = 0 added = 0
attempts = 0 attempts = 0
max_attempts = num_cycles * 20 max_attempts = num_cycles * 20
while added < num_cycles and attempts < max_attempts: while added < num_cycles and attempts < max_attempts:
attempts += 1 attempts += 1
x, y = random.randint(0, size-1), random.randint(0, size-1) x, y = random.randint(0, size-1), random.randint(0, size-1)
dirs = list(DIRECTIONS.items()) dirs = list(DIRECTIONS.items())
random.shuffle(dirs) random.shuffle(dirs)
for d, (dx, dy, opposite) in dirs: for d, (dx, dy, opposite) in dirs:
nx, ny = x + dx, y + dy nx, ny = x + dx, y + dy
if in_bounds(nx, ny, size) and d not in conns[y][x]: if in_bounds(nx, ny, size) and d not in conns[y][x]:
conns[y][x].add(d) conns[y][x].add(d)
conns[ny][nx].add(opposite) conns[ny][nx].add(opposite)
added += 1 added += 1
break break
return conns return conns
def pick_random_cells(size, count, avoid=None): def pick_random_cells(size, count, avoid=None):
all_cells = [(x, y) for y in range(size) for x in range(size)] all_cells = [(x, y) for y in range(size) for x in range(size)]
if avoid: if avoid:
all_cells = [c for c in all_cells if c not in avoid] all_cells = [c for c in all_cells if c not in avoid]
random.shuffle(all_cells) random.shuffle(all_cells)
return all_cells[:count] return all_cells[:count]
def generate_spanning_tree_with_dead_ends(size, num_dead_ends): def generate_spanning_tree_with_dead_ends(size, num_dead_ends):
if num_dead_ends > size * size - 1: if num_dead_ends > size * size - 1:
num_dead_ends = size * size - 1 num_dead_ends = size * size - 1
grid = [[set() for _ in range(size)] for _ in range(size)] grid = [[set() for _ in range(size)] for _ in range(size)]
all_cells = [(x, y) for y in range(size) for x in range(size)] all_cells = [(x, y) for y in range(size) for x in range(size)]
random.shuffle(all_cells) random.shuffle(all_cells)
start = all_cells[0] start = all_cells[0]
stack = [start] stack = [start]
visited = {start} visited = {start}
leaf_candidates = [] leaf_candidates = []
while len(visited) < size * size: while len(visited) < size * size:
if not stack: if not stack:
unvisited = [c for c in all_cells if c not in visited] unvisited = [c for c in all_cells if c not in visited]
if unvisited: if unvisited:
stack.append(unvisited[0]) stack.append(unvisited[0])
visited.add(unvisited[0]) visited.add(unvisited[0])
x, y = stack[-1] x, y = stack[-1]
dirs = list(DIRECTIONS.items()) dirs = list(DIRECTIONS.items())
random.shuffle(dirs) random.shuffle(dirs)
found = False found = False
for d, (dx, dy, opposite) in dirs: for d, (dx, dy, opposite) in dirs:
nx, ny = x + dx, y + dy nx, ny = x + dx, y + dy
if in_bounds(nx, ny, size) and (nx, ny) not in visited: if in_bounds(nx, ny, size) and (nx, ny) not in visited:
grid[y][x].add(d) grid[y][x].add(d)
grid[ny][nx].add(opposite) grid[ny][nx].add(opposite)
visited.add((nx, ny)) visited.add((nx, ny))
stack.append((nx, ny)) stack.append((nx, ny))
found = True found = True
break break
if not found: if not found:
if len(grid[y][x]) == 1: if len(grid[y][x]) == 1:
leaf_candidates.append((x, y)) leaf_candidates.append((x, y))
stack.pop() stack.pop()
leaf_nodes = leaf_candidates[:num_dead_ends] leaf_nodes = leaf_candidates[:num_dead_ends]
for y in range(size): for y in range(size):
for x in range(size): for x in range(size):
if (x, y) not in leaf_nodes and len(grid[y][x]) < 2: if (x, y) not in leaf_nodes and len(grid[y][x]) < 2:
dirs = list(DIRECTIONS.items()) dirs = list(DIRECTIONS.items())
random.shuffle(dirs) random.shuffle(dirs)
for d, (dx, dy, opposite) in dirs: for d, (dx, dy, opposite) in dirs:
nx, ny = x + dx, y + dy nx, ny = x + dx, y + dy
if in_bounds(nx, ny, size) and d not in grid[y][x]: if in_bounds(nx, ny, size) and d not in grid[y][x]:
grid[y][x].add(d) grid[y][x].add(d)
grid[ny][nx].add(opposite) grid[ny][nx].add(opposite)
if len(grid[y][x]) >= 2: if len(grid[y][x]) >= 2:
break break
return grid, leaf_nodes return grid, leaf_nodes
def generate_map(size, source_count, house_count, cycles=15): def generate_map(size, source_count, house_count, cycles=15):
conns, dead_ends = generate_spanning_tree_with_dead_ends(size, house_count) conns, dead_ends = generate_spanning_tree_with_dead_ends(size, house_count)
conns = add_cycles(conns, cycles) conns = add_cycles(conns, cycles)
houses = dead_ends[:house_count] houses = dead_ends[:house_count]
available_cells = [(x, y) for y in range(size) for x in range(size) if (x, y) not in houses] available_cells = [(x, y) for y in range(size) for x in range(size) if (x, y) not in houses]
random.shuffle(available_cells) random.shuffle(available_cells)
sources = available_cells[:source_count] sources = available_cells[:source_count]
grid = [] grid = []
for y in range(size): for y in range(size):
grid.append([]) grid.append([])
for x in range(size): for x in range(size):
if (x, y) in sources: if (x, y) in sources:
grid[-1].append("power_source") grid[-1].append("power_source")
elif (x, y) in houses: elif (x, y) in houses:
grid[-1].append("house") grid[-1].append("house")
else: else:
grid[-1].append(classify_tile(conns[y][x])) grid[-1].append(classify_tile(conns[y][x]))
return grid return grid

View File

@@ -1,150 +1,150 @@
import arcade, arcade.gui, json, time import arcade, arcade.gui, json, time
from utils.constants import button_style, NEIGHBOURS from utils.constants import button_style, NEIGHBOURS
from utils.preload import button_texture, button_hovered_texture from utils.preload import button_texture, button_hovered_texture
from collections import deque from collections import deque
from game.level_generator import generate_map from game.level_generator import generate_map
from game.cells import * from game.cells import *
class Game(arcade.gui.UIView): class Game(arcade.gui.UIView):
def __init__(self, pypresence_client, grid_size, source_count=None, house_count=None): def __init__(self, pypresence_client, grid_size, source_count=None, house_count=None):
super().__init__() super().__init__()
self.pypresence_client = pypresence_client self.pypresence_client = pypresence_client
self.pypresence_client.update(state='In Game', start=self.pypresence_client.start_time) self.pypresence_client.update(state='In Game', start=self.pypresence_client.start_time)
self.grid_size = grid_size self.grid_size = grid_size
self.source_count = source_count self.source_count = source_count
self.house_count = house_count self.house_count = house_count
self.start = time.perf_counter() self.start = time.perf_counter()
self.wire_rotations = 0 self.wire_rotations = 0
self.cells = [] self.cells = []
self.power_sources = [] self.power_sources = []
self.houses = [] self.houses = []
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1))) self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
self.map = generate_map(self.grid_size, int((self.grid_size * self.grid_size) / 10) if not source_count else source_count, int((self.grid_size * self.grid_size) / 5) if not house_count else house_count) self.map = generate_map(self.grid_size, int((self.grid_size * self.grid_size) / 10) if not source_count else source_count, int((self.grid_size * self.grid_size) / 5) if not house_count else house_count)
self.spritelist = arcade.SpriteList() self.spritelist = arcade.SpriteList()
with open("settings.json", "r") as file: with open("settings.json", "r") as file:
self.settings = json.load(file) self.settings = json.load(file)
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button.on_click = lambda event: self.main_exit() self.back_button.on_click = lambda event: self.main_exit()
self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5) self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
self.won_label = self.anchor.add(arcade.gui.UILabel(text="You won!", font_size=48), anchor_x="center", anchor_y="center") self.won_label = self.anchor.add(arcade.gui.UILabel(text="You won!", font_size=48), anchor_x="center", anchor_y="center")
self.won_label.visible = False self.won_label.visible = False
self.info_label = self.anchor.add(arcade.gui.UILabel("Time spent: 0s Wire Rotations: 0", font_size=24), anchor_x="center", anchor_y="top") self.info_label = self.anchor.add(arcade.gui.UILabel("Time spent: 0s Wire Rotations: 0", font_size=24), anchor_x="center", anchor_y="top")
x = (self.window.width / 2) - (self.grid_size * 64) / 2 x = (self.window.width / 2) - (self.grid_size * 64) / 2
y = (self.window.height / 2) + (self.grid_size * 64) / 2 y = (self.window.height / 2) + (self.grid_size * 64) / 2
for row in range(self.grid_size): for row in range(self.grid_size):
self.cells.append([]) self.cells.append([])
for col in range(self.grid_size): for col in range(self.grid_size):
left_neighbour = self.cells[row][col - 1] if col > 0 else None left_neighbour = self.cells[row][col - 1] if col > 0 else None
top_neighbour = self.cells[row - 1][col] if row > 0 else None top_neighbour = self.cells[row - 1][col] if row > 0 else None
cell_type = self.map[row][col] cell_type = self.map[row][col]
if cell_type in ["line", "corner", "t_junction", "cross"]: if cell_type in ["line", "corner", "t_junction", "cross"]:
cell = PowerLine(cell_type, x, y, left_neighbour, top_neighbour) cell = PowerLine(cell_type, x, y, left_neighbour, top_neighbour)
elif cell_type == "power_source": elif cell_type == "power_source":
cell = PowerSource(x, y, left_neighbour, top_neighbour) cell = PowerSource(x, y, left_neighbour, top_neighbour)
self.power_sources.append(cell) self.power_sources.append(cell)
elif cell_type == "house": elif cell_type == "house":
cell = House(x, y, left_neighbour, top_neighbour) cell = House(x, y, left_neighbour, top_neighbour)
self.houses.append(cell) self.houses.append(cell)
self.spritelist.append(cell) self.spritelist.append(cell)
self.cells[row].append(cell) self.cells[row].append(cell)
if left_neighbour: if left_neighbour:
left_neighbour.right_neighbour = cell left_neighbour.right_neighbour = cell
if top_neighbour: if top_neighbour:
top_neighbour.bottom_neighbour = cell top_neighbour.bottom_neighbour = cell
x += 64 x += 64
x = (self.window.width / 2) - (self.grid_size * 64) / 2 x = (self.window.width / 2) - (self.grid_size * 64) / 2
y -= 64 y -= 64
arcade.schedule(self.update_grid, 1 / 8) arcade.schedule(self.update_grid, 1 / 8)
def update_grid(self, _): def update_grid(self, _):
for row in self.cells: for row in self.cells:
for power_line in row: for power_line in row:
if power_line.cell_type != "power_source": if power_line.cell_type != "power_source":
power_line.powered = False power_line.powered = False
queue = deque(self.power_sources) queue = deque(self.power_sources)
visited = set() visited = set()
while queue: while queue:
current = queue.popleft() current = queue.popleft()
if id(current) in visited: if id(current) in visited:
continue continue
visited.add(id(current)) visited.add(id(current))
current.powered = True current.powered = True
for connected_neighbour in current.get_connected_neighbours(): for connected_neighbour in current.get_connected_neighbours():
if id(connected_neighbour) not in visited: if id(connected_neighbour) not in visited:
queue.append(connected_neighbour) queue.append(connected_neighbour)
for row in self.cells: for row in self.cells:
for cell in row: for cell in row:
cell.update_visual() cell.update_visual()
self.check_win() self.check_win()
def check_win(self): def check_win(self):
for row in self.cells: for row in self.cells:
for cell in row: for cell in row:
if cell.cell_type == "power_source": if cell.cell_type == "power_source":
continue continue
elif cell.cell_type == "house": elif cell.cell_type == "house":
if not len(cell.get_connected_neighbours()) >= 1: if not len(cell.get_connected_neighbours()) >= 1:
return return
else: else:
continue continue
if len(cell.get_connected_neighbours(True)) != len(NEIGHBOURS[cell.rotation]): if len(cell.get_connected_neighbours(True)) != len(NEIGHBOURS[cell.rotation]):
return return
self.won_label.visible = True self.won_label.visible = True
self.spritelist.visible = False self.spritelist.visible = False
arcade.unschedule(self.update_grid) arcade.unschedule(self.update_grid)
def on_mouse_press(self, x, y, button, modifiers): def on_mouse_press(self, x, y, button, modifiers):
for row in self.cells: for row in self.cells:
for cell in row: for cell in row:
if cell.cell_type in ["house", "power_source"]: if cell.cell_type in ["house", "power_source"]:
continue continue
if cell.rect.point_in_rect((x, y)): if cell.rect.point_in_rect((x, y)):
self.wire_rotations += 1 self.wire_rotations += 1
cell.next_rotation(self.settings["sfx"], self.settings.get("sfx_volume", 50)) cell.next_rotation(self.settings["sfx"], self.settings.get("sfx_volume", 50))
def on_draw(self): def on_draw(self):
super().on_draw() super().on_draw()
self.spritelist.draw() self.spritelist.draw()
def on_update(self, delta_time): def on_update(self, delta_time):
self.info_label.text = f"Time left: {int(time.perf_counter() - self.start)}s Wire Rotations: {self.wire_rotations}" self.info_label.text = f"Time left: {int(time.perf_counter() - self.start)}s Wire Rotations: {self.wire_rotations}"
def main_exit(self): def main_exit(self):
from menus.main import Main from menus.main import Main
self.window.show_view(Main(self.pypresence_client)) self.window.show_view(Main(self.pypresence_client))

View File

@@ -1,41 +1,41 @@
import arcade, arcade.gui import arcade, arcade.gui
from utils.constants import CUSTOM_DIFFICULTY_SETTINGS, slider_style, button_style from utils.constants import CUSTOM_DIFFICULTY_SETTINGS, slider_style, button_style
from utils.preload import button_texture, button_hovered_texture from utils.preload import button_texture, button_hovered_texture
class CustomDifficulty(arcade.gui.UIView): class CustomDifficulty(arcade.gui.UIView):
def __init__(self, pypresence_client): def __init__(self, pypresence_client):
super().__init__() super().__init__()
self.pypresence_client = pypresence_client self.pypresence_client = pypresence_client
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1))) self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
self.box = self.anchor.add(arcade.gui.UIBoxLayout(size_between=self.window.height / 10), anchor_x="center", anchor_y="top") self.box = self.anchor.add(arcade.gui.UIBoxLayout(size_between=self.window.height / 10), anchor_x="center", anchor_y="top")
self.custom_settings = {} self.custom_settings = {}
self.custom_setting_labels = {} self.custom_setting_labels = {}
def set_custom_setting(self, key, value): def set_custom_setting(self, key, value):
value = int(value) value = int(value)
self.custom_settings[key] = value self.custom_settings[key] = value
self.custom_setting_labels[key].text = f"{next(setting_list[1] for setting_list in CUSTOM_DIFFICULTY_SETTINGS if setting_list[0] == key)}: {value}" self.custom_setting_labels[key].text = f"{next(setting_list[1] for setting_list in CUSTOM_DIFFICULTY_SETTINGS if setting_list[0] == key)}: {value}"
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.box.add(arcade.gui.UILabel(text="Custom Difficulty Selector", font_size=32)) self.box.add(arcade.gui.UILabel(text="Custom Difficulty Selector", font_size=32))
self.box.add(arcade.gui.UISpace(height=self.window.height / 20)) self.box.add(arcade.gui.UISpace(height=self.window.height / 20))
for custom_setting_key, custom_setting_name, min_value, max_value in CUSTOM_DIFFICULTY_SETTINGS: for custom_setting_key, custom_setting_name, min_value, max_value in CUSTOM_DIFFICULTY_SETTINGS:
self.custom_settings[custom_setting_key] = int((max_value - min_value) / 2) self.custom_settings[custom_setting_key] = int((max_value - min_value) / 2)
self.custom_setting_labels[custom_setting_key] = self.box.add(arcade.gui.UILabel(text=f"{custom_setting_name}: {int((max_value - min_value) / 2)}", font_size=28)) self.custom_setting_labels[custom_setting_key] = self.box.add(arcade.gui.UILabel(text=f"{custom_setting_name}: {int((max_value - min_value) / 2)}", font_size=28))
slider = self.box.add(arcade.gui.UISlider(step=1, min_value=min_value, max_value=max_value, value=int((max_value - min_value) / 2), style=slider_style, width=self.window.width / 2, height=self.window.height / 15)) slider = self.box.add(arcade.gui.UISlider(step=1, min_value=min_value, max_value=max_value, value=int((max_value - min_value) / 2), style=slider_style, width=self.window.width / 2, height=self.window.height / 15))
slider._render_steps = lambda surface: None slider._render_steps = lambda surface: None
slider.on_change = lambda event, key=custom_setting_key: self.set_custom_setting(key, event.new_value) slider.on_change = lambda event, key=custom_setting_key: self.set_custom_setting(key, event.new_value)
self.play_button = self.anchor.add(arcade.gui.UITextureButton(text="Play", style=button_style, texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10), anchor_x="center", anchor_y="bottom") self.play_button = self.anchor.add(arcade.gui.UITextureButton(text="Play", style=button_style, texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10), anchor_x="center", anchor_y="bottom")
self.play_button.on_click = lambda event: self.play() self.play_button.on_click = lambda event: self.play()
def play(self): def play(self):
from game.play import Game from game.play import Game
self.window.show_view(Game(self.pypresence_client, self.custom_settings["size"], self.custom_settings["source_count"], self.custom_settings["house_count"])) self.window.show_view(Game(self.pypresence_client, self.custom_settings["size"], self.custom_settings["source_count"], self.custom_settings["house_count"]))

View File

@@ -1,43 +1,43 @@
import arcade, arcade.gui import arcade, arcade.gui
from utils.preload import button_texture, button_hovered_texture from utils.preload import button_texture, button_hovered_texture
from utils.constants import big_button_style, button_style from utils.constants import big_button_style, button_style
class DifficultySelector(arcade.gui.UIView): class DifficultySelector(arcade.gui.UIView):
def __init__(self, pypresence_client): def __init__(self, pypresence_client):
super().__init__() super().__init__()
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout()) self.anchor = self.add_widget(arcade.gui.UIAnchorLayout())
self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x='center', anchor_y='center') self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x='center', anchor_y='center')
self.pypresence_client = pypresence_client self.pypresence_client = pypresence_client
self.pypresence_client.update(state='In Menu', details='In Difficulty Selector', start=self.pypresence_client.start_time) self.pypresence_client.update(state='In Menu', details='In Difficulty Selector', start=self.pypresence_client.start_time)
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button.on_click = lambda event: self.main_exit() self.back_button.on_click = lambda event: self.main_exit()
self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5) self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
self.box.add(arcade.gui.UILabel(text="Difficulty Selector", font_size=32)) self.box.add(arcade.gui.UILabel(text="Difficulty Selector", font_size=32))
for difficulty in ["7x7", "8x8", "9x9", "10x10", "11x11", "12x12", "Custom"]: for difficulty in ["7x7", "8x8", "9x9", "10x10", "11x11", "12x12", "Custom"]:
button = self.box.add(arcade.gui.UITextureButton(text=difficulty, width=self.window.width / 2, height=self.window.height / 10, texture=button_texture, texture_hovered=button_hovered_texture, style=big_button_style)) button = self.box.add(arcade.gui.UITextureButton(text=difficulty, width=self.window.width / 2, height=self.window.height / 10, texture=button_texture, texture_hovered=button_hovered_texture, style=big_button_style))
if not difficulty == "Custom": if not difficulty == "Custom":
button.on_click = lambda e, difficulty=difficulty: self.play(int(difficulty.split("x")[0])) button.on_click = lambda e, difficulty=difficulty: self.play(int(difficulty.split("x")[0]))
else: else:
button.on_click = lambda e: self.custom_difficulty() button.on_click = lambda e: self.custom_difficulty()
def custom_difficulty(self): def custom_difficulty(self):
from menus.custom_difficulty import CustomDifficulty from menus.custom_difficulty import CustomDifficulty
self.window.show_view(CustomDifficulty(self.pypresence_client)) self.window.show_view(CustomDifficulty(self.pypresence_client))
def play(self, difficulty): def play(self, difficulty):
from game.play import Game from game.play import Game
self.window.show_view(Game(self.pypresence_client, difficulty)) self.window.show_view(Game(self.pypresence_client, difficulty))
def main_exit(self): def main_exit(self):
from menus.main import Main from menus.main import Main
self.window.show_view(Main(self.pypresence_client)) self.window.show_view(Main(self.pypresence_client))

View File

@@ -1,74 +1,74 @@
import arcade, arcade.gui, asyncio, pypresence, time, copy, json import arcade, arcade.gui, asyncio, pypresence, time, copy, json
from utils.preload import button_texture, button_hovered_texture from utils.preload import button_texture, button_hovered_texture
from utils.constants import big_button_style, discord_presence_id from utils.constants import big_button_style, discord_presence_id
from utils.utils import FakePyPresence from utils.utils import FakePyPresence
class Main(arcade.gui.UIView): class Main(arcade.gui.UIView):
def __init__(self, pypresence_client=None): def __init__(self, pypresence_client=None):
super().__init__() super().__init__()
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout()) self.anchor = self.add_widget(arcade.gui.UIAnchorLayout())
self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x='center', anchor_y='center') self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=10), anchor_x='center', anchor_y='center')
self.pypresence_client = pypresence_client self.pypresence_client = pypresence_client
with open("settings.json", "r") as file: with open("settings.json", "r") as file:
self.settings_dict = json.load(file) self.settings_dict = json.load(file)
if self.settings_dict.get('discord_rpc', True): if self.settings_dict.get('discord_rpc', True):
if self.pypresence_client == None: # Game has started if self.pypresence_client == None: # Game has started
try: try:
asyncio.get_event_loop() asyncio.get_event_loop()
except: except:
asyncio.set_event_loop(asyncio.new_event_loop()) asyncio.set_event_loop(asyncio.new_event_loop())
try: try:
self.pypresence_client = pypresence.Presence(discord_presence_id) self.pypresence_client = pypresence.Presence(discord_presence_id)
self.pypresence_client.connect() self.pypresence_client.connect()
self.pypresence_client.start_time = time.time() self.pypresence_client.start_time = time.time()
except: except:
self.pypresence_client = FakePyPresence() self.pypresence_client = FakePyPresence()
self.pypresence_client.start_time = time.time() self.pypresence_client.start_time = time.time()
elif isinstance(self.pypresence_client, FakePyPresence): # the user has enabled RPC in the settings in this session. elif isinstance(self.pypresence_client, FakePyPresence): # the user has enabled RPC in the settings in this session.
# get start time from old object # get start time from old object
start_time = copy.deepcopy(self.pypresence_client.start_time) start_time = copy.deepcopy(self.pypresence_client.start_time)
try: try:
self.pypresence_client = pypresence.Presence(discord_presence_id) self.pypresence_client = pypresence.Presence(discord_presence_id)
self.pypresence_client.connect() self.pypresence_client.connect()
self.pypresence_client.start_time = start_time self.pypresence_client.start_time = start_time
except: except:
self.pypresence_client = FakePyPresence() self.pypresence_client = FakePyPresence()
self.pypresence_client.start_time = start_time self.pypresence_client.start_time = start_time
self.pypresence_client.update(state='In Menu', details='In Main Menu', start=self.pypresence_client.start_time) self.pypresence_client.update(state='In Menu', details='In Main Menu', start=self.pypresence_client.start_time)
else: # game has started, but the user has disabled RPC in the settings. else: # game has started, but the user has disabled RPC in the settings.
self.pypresence_client = FakePyPresence() self.pypresence_client = FakePyPresence()
self.pypresence_client.start_time = time.time() self.pypresence_client.start_time = time.time()
self.pypresence_client.update(state='In Menu', details='In Main Menu', start=self.pypresence_client.start_time) self.pypresence_client.update(state='In Menu', details='In Main Menu', start=self.pypresence_client.start_time)
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.title_label = self.box.add(arcade.gui.UILabel(text="Connect the Current", font_name="Roboto", font_size=48)) self.title_label = self.box.add(arcade.gui.UILabel(text="Connect the Current", font_name="Roboto", font_size=48))
self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style))
self.play_button.on_click = lambda event: self.play() self.play_button.on_click = lambda event: self.play()
self.tutorial_button = self.box.add(arcade.gui.UITextureButton(text="Tutorial", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) self.tutorial_button = self.box.add(arcade.gui.UITextureButton(text="Tutorial", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style))
self.tutorial_button.on_click = lambda event: self.tutorial() self.tutorial_button.on_click = lambda event: self.tutorial()
self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style))
self.settings_button.on_click = lambda event: self.settings() self.settings_button.on_click = lambda event: self.settings()
def play(self): def play(self):
from menus.difficulty_selector import DifficultySelector from menus.difficulty_selector import DifficultySelector
self.window.show_view(DifficultySelector(self.pypresence_client)) self.window.show_view(DifficultySelector(self.pypresence_client))
def tutorial(self): def tutorial(self):
from menus.tutorial import Tutorial from menus.tutorial import Tutorial
self.window.show_view(Tutorial(self.pypresence_client)) self.window.show_view(Tutorial(self.pypresence_client))
def settings(self): def settings(self):
from menus.settings import Settings from menus.settings import Settings
self.window.show_view(Settings(self.pypresence_client)) self.window.show_view(Settings(self.pypresence_client))

View File

@@ -1,284 +1,284 @@
import copy, pypresence, json, os import copy, pypresence, json, os
import arcade, arcade.gui import arcade, arcade.gui
from utils.constants import button_style, dropdown_style, slider_style, settings, discord_presence_id, settings_start_category from utils.constants import button_style, dropdown_style, slider_style, settings, discord_presence_id, settings_start_category
from utils.utils import FakePyPresence from utils.utils import FakePyPresence
from utils.preload import button_texture, button_hovered_texture from utils.preload import button_texture, button_hovered_texture
from arcade.gui import UIBoxLayout, UIAnchorLayout from arcade.gui import UIBoxLayout, UIAnchorLayout
class Settings(arcade.gui.UIView): class Settings(arcade.gui.UIView):
def __init__(self, pypresence_client, *args): def __init__(self, pypresence_client, *args):
super().__init__() super().__init__()
self.args = args self.args = args
with open("settings.json", "r") as file: with open("settings.json", "r") as file:
self.settings_dict = json.load(file) self.settings_dict = json.load(file)
self.pypresence_client = pypresence_client self.pypresence_client = pypresence_client
self.pypresence_client.update(state='In Settings', details='Modifying Settings', start=self.pypresence_client.start_time) self.pypresence_client.update(state='In Settings', details='Modifying Settings', start=self.pypresence_client.start_time)
self.slider_labels = {} self.slider_labels = {}
self.sliders = {} self.sliders = {}
self.on_radiobuttons = {} self.on_radiobuttons = {}
self.off_radiobuttons = {} self.off_radiobuttons = {}
self.current_category = settings_start_category self.current_category = settings_start_category
self.modified_settings = {} self.modified_settings = {}
def create_layouts(self): def create_layouts(self):
self.anchor = self.add_widget(UIAnchorLayout(size_hint=(1, 1))) self.anchor = self.add_widget(UIAnchorLayout(size_hint=(1, 1)))
self.box = UIBoxLayout(space_between=50, align="center", vertical=False) self.box = UIBoxLayout(space_between=50, align="center", vertical=False)
self.anchor.add(self.box, anchor_x="center", anchor_y="top", align_x=10, align_y=-75) self.anchor.add(self.box, anchor_x="center", anchor_y="top", align_x=10, align_y=-75)
self.top_box = UIBoxLayout(space_between=self.window.width / 160, vertical=False) self.top_box = UIBoxLayout(space_between=self.window.width / 160, vertical=False)
self.anchor.add(self.top_box, anchor_x="left", anchor_y="top", align_x=10, align_y=-10) self.anchor.add(self.top_box, anchor_x="left", anchor_y="top", align_x=10, align_y=-10)
self.key_layout = self.box.add(UIBoxLayout(space_between=20, align='left')) self.key_layout = self.box.add(UIBoxLayout(space_between=20, align='left'))
self.value_layout = self.box.add(UIBoxLayout(space_between=13, align='left')) self.value_layout = self.box.add(UIBoxLayout(space_between=13, align='left'))
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.create_layouts() self.create_layouts()
self.ui.push_handlers(self) self.ui.push_handlers(self)
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button.on_click = lambda event: self.main_exit() self.back_button.on_click = lambda event: self.main_exit()
self.top_box.add(self.back_button) self.top_box.add(self.back_button)
self.display_categories() self.display_categories()
self.display_category(settings_start_category) self.display_category(settings_start_category)
def display_categories(self): def display_categories(self):
for category in settings: for category in settings:
category_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=category, style=button_style, width=self.window.width / 10, height=50) category_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=category, style=button_style, width=self.window.width / 10, height=50)
if not category == "Credits": if not category == "Credits":
category_button.on_click = lambda event, category=category: self.display_category(category) category_button.on_click = lambda event, category=category: self.display_category(category)
else: else:
category_button.on_click = lambda event: self.credits() category_button.on_click = lambda event: self.credits()
self.top_box.add(category_button) self.top_box.add(category_button)
def display_category(self, category): def display_category(self, category):
if hasattr(self, 'apply_button'): if hasattr(self, 'apply_button'):
self.anchor.remove(self.apply_button) self.anchor.remove(self.apply_button)
del self.apply_button del self.apply_button
if hasattr(self, 'credits_label'): if hasattr(self, 'credits_label'):
self.anchor.remove(self.credits_label) self.anchor.remove(self.credits_label)
del self.credits_label del self.credits_label
self.current_category = category self.current_category = category
self.key_layout.clear() self.key_layout.clear()
self.value_layout.clear() self.value_layout.clear()
for setting in settings[category]: for setting in settings[category]:
label = arcade.gui.UILabel(text=setting, font_name="Roboto", font_size=28, text_color=arcade.color.WHITE ) label = arcade.gui.UILabel(text=setting, font_name="Roboto", font_size=28, text_color=arcade.color.WHITE )
self.key_layout.add(label) self.key_layout.add(label)
setting_dict = settings[category][setting] setting_dict = settings[category][setting]
if setting_dict['type'] == "option": if setting_dict['type'] == "option":
dropdown = arcade.gui.UIDropdown(options=setting_dict['options'], width=200, height=50, default=self.settings_dict.get(setting_dict["config_key"], setting_dict["options"][0]), active_style=dropdown_style, dropdown_style=dropdown_style, primary_style=dropdown_style) dropdown = arcade.gui.UIDropdown(options=setting_dict['options'], width=200, height=50, default=self.settings_dict.get(setting_dict["config_key"], setting_dict["options"][0]), active_style=dropdown_style, dropdown_style=dropdown_style, primary_style=dropdown_style)
dropdown.on_change = lambda _, setting=setting, dropdown=dropdown: self.update(setting, dropdown.value, "option") dropdown.on_change = lambda _, setting=setting, dropdown=dropdown: self.update(setting, dropdown.value, "option")
self.value_layout.add(dropdown) self.value_layout.add(dropdown)
elif setting_dict['type'] == "bool": elif setting_dict['type'] == "bool":
button_layout = self.value_layout.add(arcade.gui.UIBoxLayout(space_between=50, vertical=False)) button_layout = self.value_layout.add(arcade.gui.UIBoxLayout(space_between=50, vertical=False))
on_radiobutton = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="ON", style=button_style, width=150, height=50) on_radiobutton = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="ON", style=button_style, width=150, height=50)
self.on_radiobuttons[setting] = on_radiobutton self.on_radiobuttons[setting] = on_radiobutton
on_radiobutton.on_click = lambda _, setting=setting: self.update(setting, True, "bool") on_radiobutton.on_click = lambda _, setting=setting: self.update(setting, True, "bool")
button_layout.add(on_radiobutton) button_layout.add(on_radiobutton)
off_radiobutton = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="OFF", style=button_style, width=150, height=50) off_radiobutton = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="OFF", style=button_style, width=150, height=50)
self.off_radiobuttons[setting] = off_radiobutton self.off_radiobuttons[setting] = off_radiobutton
off_radiobutton.on_click = lambda _, setting=setting: self.update(setting, False, "bool") off_radiobutton.on_click = lambda _, setting=setting: self.update(setting, False, "bool")
button_layout.add(off_radiobutton) button_layout.add(off_radiobutton)
if self.settings_dict.get(setting_dict["config_key"], setting_dict["default"]): if self.settings_dict.get(setting_dict["config_key"], setting_dict["default"]):
self.set_highlighted_style(on_radiobutton) self.set_highlighted_style(on_radiobutton)
self.set_normal_style(off_radiobutton) self.set_normal_style(off_radiobutton)
else: else:
self.set_highlighted_style(off_radiobutton) self.set_highlighted_style(off_radiobutton)
self.set_normal_style(on_radiobutton) self.set_normal_style(on_radiobutton)
elif setting_dict['type'] == "slider": elif setting_dict['type'] == "slider":
if setting == "FPS Limit": if setting == "FPS Limit":
if self.settings_dict.get(setting_dict["config_key"]) == 0: if self.settings_dict.get(setting_dict["config_key"]) == 0:
label_text = "FPS Limit: Disabled" label_text = "FPS Limit: Disabled"
else: else:
label_text = f"FPS Limit: {self.settings_dict.get(setting_dict['config_key'], setting_dict['default'])}" label_text = f"FPS Limit: {self.settings_dict.get(setting_dict['config_key'], setting_dict['default'])}"
else: else:
label_text = f"{setting}: {int(self.settings_dict.get(setting_dict['config_key'], setting_dict['default']))}" label_text = f"{setting}: {int(self.settings_dict.get(setting_dict['config_key'], setting_dict['default']))}"
label.text = label_text label.text = label_text
self.slider_labels[setting] = label self.slider_labels[setting] = label
slider = arcade.gui.UISlider(width=400, height=50, value=self.settings_dict.get(setting_dict["config_key"], setting_dict["default"]), min_value=setting_dict['min'], max_value=setting_dict['max'], style=slider_style) slider = arcade.gui.UISlider(width=400, height=50, value=self.settings_dict.get(setting_dict["config_key"], setting_dict["default"]), min_value=setting_dict['min'], max_value=setting_dict['max'], style=slider_style)
slider.on_change = lambda _, setting=setting, slider=slider: self.update(setting, slider.value, "slider") slider.on_change = lambda _, setting=setting, slider=slider: self.update(setting, slider.value, "slider")
self.sliders[setting] = slider self.sliders[setting] = slider
self.value_layout.add(slider) self.value_layout.add(slider)
self.apply_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Apply', style=button_style, width=200, height=100) self.apply_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='Apply', style=button_style, width=200, height=100)
self.apply_button.on_click = lambda event: self.apply_settings() self.apply_button.on_click = lambda event: self.apply_settings()
self.anchor.add(self.apply_button, anchor_x="right", anchor_y="bottom", align_x=-10, align_y=10) self.anchor.add(self.apply_button, anchor_x="right", anchor_y="bottom", align_x=-10, align_y=10)
def apply_settings(self): def apply_settings(self):
for config_key, value in self.modified_settings.items(): for config_key, value in self.modified_settings.items():
self.settings_dict[config_key] = value self.settings_dict[config_key] = value
if self.settings_dict['window_mode'] == "Fullscreen": if self.settings_dict['window_mode'] == "Fullscreen":
self.window.set_fullscreen(True) self.window.set_fullscreen(True)
else: else:
self.window.set_fullscreen(False) self.window.set_fullscreen(False)
width, height = map(int, self.settings_dict['resolution'].split('x')) width, height = map(int, self.settings_dict['resolution'].split('x'))
self.window.set_size(width, height) self.window.set_size(width, height)
if self.settings_dict['vsync']: if self.settings_dict['vsync']:
self.window.set_vsync(True) self.window.set_vsync(True)
display_mode = self.window.display.get_default_screen().get_mode() display_mode = self.window.display.get_default_screen().get_mode()
refresh_rate = display_mode.rate refresh_rate = display_mode.rate
self.window.set_update_rate(1 / refresh_rate) self.window.set_update_rate(1 / refresh_rate)
self.window.set_draw_rate(1 / refresh_rate) self.window.set_draw_rate(1 / refresh_rate)
elif not self.settings_dict['fps_limit'] == 0: elif not self.settings_dict['fps_limit'] == 0:
self.window.set_vsync(False) self.window.set_vsync(False)
self.window.set_update_rate(1 / self.settings_dict['fps_limit']) self.window.set_update_rate(1 / self.settings_dict['fps_limit'])
self.window.set_draw_rate(1 / self.settings_dict['fps_limit']) self.window.set_draw_rate(1 / self.settings_dict['fps_limit'])
else: else:
self.window.set_vsync(False) self.window.set_vsync(False)
self.window.set_update_rate(1 / 99999999) self.window.set_update_rate(1 / 99999999)
self.window.set_draw_rate(1 / 99999999) self.window.set_draw_rate(1 / 99999999)
if self.settings_dict['discord_rpc']: if self.settings_dict['discord_rpc']:
if isinstance(self.pypresence_client, FakePyPresence): # the user has enabled RPC in the settings in this session. if isinstance(self.pypresence_client, FakePyPresence): # the user has enabled RPC in the settings in this session.
start_time = copy.deepcopy(self.pypresence_client.start_time) start_time = copy.deepcopy(self.pypresence_client.start_time)
self.pypresence_client.close() self.pypresence_client.close()
del self.pypresence_client del self.pypresence_client
try: try:
self.pypresence_client = pypresence.Presence(discord_presence_id) self.pypresence_client = pypresence.Presence(discord_presence_id)
self.pypresence_client.connect() self.pypresence_client.connect()
self.pypresence_client.update(state='In Settings', details='Modifying Settings', start=start_time) self.pypresence_client.update(state='In Settings', details='Modifying Settings', start=start_time)
self.pypresence_client.start_time = start_time self.pypresence_client.start_time = start_time
except: except:
self.pypresence_client = FakePyPresence() self.pypresence_client = FakePyPresence()
self.pypresence_client.start_time = start_time self.pypresence_client.start_time = start_time
else: else:
if not isinstance(self.pypresence_client, FakePyPresence): if not isinstance(self.pypresence_client, FakePyPresence):
start_time = copy.deepcopy(self.pypresence_client.start_time) start_time = copy.deepcopy(self.pypresence_client.start_time)
self.pypresence_client.update() self.pypresence_client.update()
self.pypresence_client.close() self.pypresence_client.close()
del self.pypresence_client del self.pypresence_client
self.pypresence_client = FakePyPresence() self.pypresence_client = FakePyPresence()
self.pypresence_client.start_time = start_time self.pypresence_client.start_time = start_time
self.ui_cleanup() self.ui_cleanup()
self.ui = arcade.gui.UIManager() self.ui = arcade.gui.UIManager()
self.ui.enable() self.ui.enable()
self.create_layouts() self.create_layouts()
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button.on_click = lambda event: self.main_exit() self.back_button.on_click = lambda event: self.main_exit()
self.top_box.add(self.back_button) self.top_box.add(self.back_button)
self.display_categories() self.display_categories()
self.display_category(self.current_category) self.display_category(self.current_category)
with open("settings.json", "w") as file: with open("settings.json", "w") as file:
file.write(json.dumps(self.settings_dict, indent=4)) file.write(json.dumps(self.settings_dict, indent=4))
def update(self, setting=None, button_state=None, setting_type="bool"): def update(self, setting=None, button_state=None, setting_type="bool"):
setting_dict = settings[self.current_category][setting] setting_dict = settings[self.current_category][setting]
config_key = settings[self.current_category][setting]["config_key"] config_key = settings[self.current_category][setting]["config_key"]
if setting_type == "option": if setting_type == "option":
self.modified_settings[config_key] = button_state self.modified_settings[config_key] = button_state
elif setting_type == "bool": elif setting_type == "bool":
self.modified_settings[config_key] = button_state self.modified_settings[config_key] = button_state
if button_state: if button_state:
self.set_highlighted_style(self.on_radiobuttons[setting]) self.set_highlighted_style(self.on_radiobuttons[setting])
self.set_normal_style(self.off_radiobuttons[setting]) self.set_normal_style(self.off_radiobuttons[setting])
else: else:
self.set_highlighted_style(self.off_radiobuttons[setting]) self.set_highlighted_style(self.off_radiobuttons[setting])
self.set_normal_style(self.on_radiobuttons[setting]) self.set_normal_style(self.on_radiobuttons[setting])
elif setting_type == "slider": elif setting_type == "slider":
new_value = int(button_state) new_value = int(button_state)
self.modified_settings[config_key] = new_value self.modified_settings[config_key] = new_value
self.sliders[setting].value = new_value self.sliders[setting].value = new_value
if setting == "FPS Limit": if setting == "FPS Limit":
if new_value == 0: if new_value == 0:
label_text = "FPS Limit: Disabled" label_text = "FPS Limit: Disabled"
else: else:
label_text = f"FPS Limit: {str(new_value).rjust(8)}" label_text = f"FPS Limit: {str(new_value).rjust(8)}"
else: else:
label_text = f"{setting}: {str(new_value).rjust(8)}" label_text = f"{setting}: {str(new_value).rjust(8)}"
self.slider_labels[setting].text = label_text self.slider_labels[setting].text = label_text
def credits(self): def credits(self):
if hasattr(self, 'apply_button'): if hasattr(self, 'apply_button'):
self.anchor.remove(self.apply_button) self.anchor.remove(self.apply_button)
del self.apply_button del self.apply_button
if hasattr(self, 'credits_label'): if hasattr(self, 'credits_label'):
self.anchor.remove(self.credits_label) self.anchor.remove(self.credits_label)
del self.credits_label del self.credits_label
self.key_layout.clear() self.key_layout.clear()
self.value_layout.clear() self.value_layout.clear()
with open('CREDITS', 'r') as file: with open('CREDITS', 'r') as file:
text = file.read() text = file.read()
if self.window.width == 3840: if self.window.width == 3840:
font_size = 30 font_size = 30
elif self.window.width == 2560: elif self.window.width == 2560:
font_size = 20 font_size = 20
elif self.window.width == 1920: elif self.window.width == 1920:
font_size = 17 font_size = 17
elif self.window.width >= 1440: elif self.window.width >= 1440:
font_size = 14 font_size = 14
else: else:
font_size = 12 font_size = 12
self.credits_label = arcade.gui.UILabel(text=text, text_color=arcade.color.WHITE, font_name="Roboto", font_size=font_size, align="center", multiline=True) self.credits_label = arcade.gui.UILabel(text=text, text_color=arcade.color.WHITE, font_name="Roboto", font_size=font_size, align="center", multiline=True)
self.key_layout.add(self.credits_label) self.key_layout.add(self.credits_label)
def set_highlighted_style(self, element): def set_highlighted_style(self, element):
element.texture = button_hovered_texture element.texture = button_hovered_texture
element.texture_hovered = button_texture element.texture_hovered = button_texture
def set_normal_style(self, element): def set_normal_style(self, element):
element.texture_hovered = button_hovered_texture element.texture_hovered = button_hovered_texture
element.texture = button_texture element.texture = button_texture
def main_exit(self): def main_exit(self):
from menus.main import Main from menus.main import Main
self.window.show_view(Main(self.pypresence_client, *self.args)) self.window.show_view(Main(self.pypresence_client, *self.args))
def ui_cleanup(self): def ui_cleanup(self):
self.ui.clear() self.ui.clear()
del self.ui del self.ui

View File

@@ -1,37 +1,37 @@
import arcade, arcade.gui import arcade, arcade.gui
from utils.preload import button_texture, button_hovered_texture from utils.preload import button_texture, button_hovered_texture
from utils.constants import button_style from utils.constants import button_style
TUTORIAL_TEXT = """ TUTORIAL_TEXT = """
In Connect the Current, you have to rotate power lines so power reaches to all of the houses. In Connect the Current, you have to rotate power lines so power reaches to all of the houses.
- Every line has to be connected on all of it's sides. - Every line has to be connected on all of it's sides.
- When needed, you might have to create loops of power or branches with no house linked to them. - When needed, you might have to create loops of power or branches with no house linked to them.
(This is also because it's randomly generated and i couldn't find a way to generate maps with no meaningless branches) (This is also because it's randomly generated and i couldn't find a way to generate maps with no meaningless branches)
- To rotate a line, just click on it and it will change its rotation. - To rotate a line, just click on it and it will change its rotation.
- Maps are randomly generated, difficulty(size, source count, house count) depends on what you pick and grows exponentially. - Maps are randomly generated, difficulty(size, source count, house count) depends on what you pick and grows exponentially.
""" """
class Tutorial(arcade.gui.UIView): class Tutorial(arcade.gui.UIView):
def __init__(self, pypresence_client): def __init__(self, pypresence_client):
super().__init__() super().__init__()
self.pypresence_client = pypresence_client self.pypresence_client = pypresence_client
self.pypresence_client.update(state="Checking Tutorial") self.pypresence_client.update(state="Checking Tutorial")
self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1))) self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1)))
self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=20), anchor_x="center", anchor_y="top") self.box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=20), anchor_x="center", anchor_y="top")
def main_exit(self): def main_exit(self):
from menus.main import Main from menus.main import Main
self.window.show_view(Main(self.pypresence_client)) self.window.show_view(Main(self.pypresence_client))
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50)
self.back_button.on_click = lambda event: self.main_exit() self.back_button.on_click = lambda event: self.main_exit()
self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5) self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5)
self.box.add(arcade.gui.UILabel(text="CTC Tutorial", font_size=40)) self.box.add(arcade.gui.UILabel(text="CTC Tutorial", font_size=40))
self.box.add(arcade.gui.UILabel(text=TUTORIAL_TEXT, font_size=20, multiline=True)) self.box.add(arcade.gui.UILabel(text=TUTORIAL_TEXT, font_size=20, multiline=True))

View File

@@ -1,10 +1,10 @@
[project] [project]
name = "connect-the-current" name = "connect-the-current"
version = "0.1.0" version = "0.1.0"
description = "Connect the Current" description = "Connect the Current"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"arcade==3.2.0", "arcade==3.2.0",
"pypresence>=4.3.0", "pypresence>=4.3.0",
] ]

View File

@@ -1,22 +1,22 @@
# This file was autogenerated by uv via the following command: # This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o requirements.txt # uv pip compile pyproject.toml -o requirements.txt
arcade==3.2.0 arcade==3.2.0
# via connect-the-current (pyproject.toml) # via connect-the-current (pyproject.toml)
attrs==25.3.0 attrs==25.3.0
# via pytiled-parser # via pytiled-parser
cffi==1.17.1 cffi==1.17.1
# via pymunk # via pymunk
pillow==11.0.0 pillow==11.0.0
# via arcade # via arcade
pycparser==2.22 pycparser==2.22
# via cffi # via cffi
pyglet==2.1.6 pyglet==2.1.6
# via arcade # via arcade
pymunk==6.9.0 pymunk==6.9.0
# via arcade # via arcade
pypresence==4.3.0 pypresence==4.3.0
# via connect-the-current (pyproject.toml) # via connect-the-current (pyproject.toml)
pytiled-parser==2.2.9 pytiled-parser==2.2.9
# via arcade # via arcade
typing-extensions==4.14.1 typing-extensions==4.14.1
# via pytiled-parser # via pytiled-parser

256
run.py
View File

@@ -1,128 +1,128 @@
import pyglet import pyglet
pyglet.options['shadow_window'] = False # Fix double window issue on Wayland pyglet.options['shadow_window'] = False # Fix double window issue on Wayland
pyglet.options.debug_gl = False pyglet.options.debug_gl = False
import logging, datetime, os, json, sys, arcade, platform import logging, datetime, os, json, sys, arcade, platform
# Set up paths BEFORE importing modules that load assets # Set up paths BEFORE importing modules that load assets
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
pyglet.resource.path.append(script_dir) pyglet.resource.path.append(script_dir)
pyglet.font.add_directory(os.path.join(script_dir, 'assets', 'fonts')) pyglet.font.add_directory(os.path.join(script_dir, 'assets', 'fonts'))
from utils.utils import get_closest_resolution, print_debug_info, on_exception from utils.utils import get_closest_resolution, print_debug_info, on_exception
from utils.constants import log_dir, menu_background_color from utils.constants import log_dir, menu_background_color
from menus.main import Main from menus.main import Main
from arcade.experimental.controller_window import ControllerWindow from arcade.experimental.controller_window import ControllerWindow
sys.excepthook = on_exception sys.excepthook = on_exception
if not log_dir in os.listdir(): if not log_dir in os.listdir():
os.makedirs(log_dir) os.makedirs(log_dir)
while len(os.listdir(log_dir)) >= 5: while len(os.listdir(log_dir)) >= 5:
files = [(file, os.path.getctime(os.path.join(log_dir, file))) for file in os.listdir(log_dir)] files = [(file, os.path.getctime(os.path.join(log_dir, file))) for file in os.listdir(log_dir)]
oldest_file = sorted(files, key=lambda x: x[1])[0][0] oldest_file = sorted(files, key=lambda x: x[1])[0][0]
os.remove(os.path.join(log_dir, oldest_file)) os.remove(os.path.join(log_dir, oldest_file))
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
log_filename = f"debug_{timestamp}.log" log_filename = f"debug_{timestamp}.log"
logging.basicConfig(filename=f'{os.path.join(log_dir, log_filename)}', format='%(asctime)s %(name)s %(levelname)s: %(message)s', level=logging.DEBUG) logging.basicConfig(filename=f'{os.path.join(log_dir, log_filename)}', format='%(asctime)s %(name)s %(levelname)s: %(message)s', level=logging.DEBUG)
for logger_name_to_disable in ['arcade']: for logger_name_to_disable in ['arcade']:
logging.getLogger(logger_name_to_disable).propagate = False logging.getLogger(logger_name_to_disable).propagate = False
logging.getLogger(logger_name_to_disable).disabled = True logging.getLogger(logger_name_to_disable).disabled = True
if os.path.exists('settings.json'): if os.path.exists('settings.json'):
with open('settings.json', 'r') as settings_file: with open('settings.json', 'r') as settings_file:
settings = json.load(settings_file) settings = json.load(settings_file)
resolution = list(map(int, settings['resolution'].split('x'))) resolution = list(map(int, settings['resolution'].split('x')))
if not settings.get("anti_aliasing", "4x MSAA") == "None": if not settings.get("anti_aliasing", "4x MSAA") == "None":
antialiasing = int(settings.get("anti_aliasing", "4x MSAA").split('x')[0]) antialiasing = int(settings.get("anti_aliasing", "4x MSAA").split('x')[0])
else: else:
antialiasing = 0 antialiasing = 0
# Wayland workaround (can be overridden with environment variable) # Wayland workaround (can be overridden with environment variable)
if (platform.system() == "Linux" and if (platform.system() == "Linux" and
os.environ.get("WAYLAND_DISPLAY") and os.environ.get("WAYLAND_DISPLAY") and
not os.environ.get("ARCADE_FORCE_MSAA")): not os.environ.get("ARCADE_FORCE_MSAA")):
logging.info("Wayland detected - disabling MSAA (set ARCADE_FORCE_MSAA=1 to override)") logging.info("Wayland detected - disabling MSAA (set ARCADE_FORCE_MSAA=1 to override)")
antialiasing = 0 antialiasing = 0
fullscreen = settings['window_mode'] == 'Fullscreen' fullscreen = settings['window_mode'] == 'Fullscreen'
style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT
vsync = settings['vsync'] vsync = settings['vsync']
fps_limit = settings['fps_limit'] fps_limit = settings['fps_limit']
else: else:
resolution = get_closest_resolution() resolution = get_closest_resolution()
antialiasing = 4 antialiasing = 4
# Wayland workaround (can be overridden with environment variable) # Wayland workaround (can be overridden with environment variable)
if (platform.system() == "Linux" and if (platform.system() == "Linux" and
os.environ.get("WAYLAND_DISPLAY") and os.environ.get("WAYLAND_DISPLAY") and
not os.environ.get("ARCADE_FORCE_MSAA")): not os.environ.get("ARCADE_FORCE_MSAA")):
logging.info("Wayland detected - disabling MSAA (set ARCADE_FORCE_MSAA=1 to override)") logging.info("Wayland detected - disabling MSAA (set ARCADE_FORCE_MSAA=1 to override)")
antialiasing = 0 antialiasing = 0
fullscreen = False fullscreen = False
style = arcade.Window.WINDOW_STYLE_DEFAULT style = arcade.Window.WINDOW_STYLE_DEFAULT
vsync = True vsync = True
fps_limit = 0 fps_limit = 0
settings = { settings = {
"music": True, "music": True,
"music_volume": 50, "music_volume": 50,
"resolution": f"{resolution[0]}x{resolution[1]}", "resolution": f"{resolution[0]}x{resolution[1]}",
"antialiasing": "4x MSAA", "antialiasing": "4x MSAA",
"window_mode": "Windowed", "window_mode": "Windowed",
"vsync": True, "vsync": True,
"fps_limit": 60, "fps_limit": 60,
"discord_rpc": True "discord_rpc": True
} }
with open("settings.json", "w") as file: with open("settings.json", "w") as file:
file.write(json.dumps(settings)) file.write(json.dumps(settings))
# if settings.get("music", True): # if settings.get("music", True):
# theme_sound.play(volume=settings.get("music_volume", 50) / 100, loop=True) # theme_sound.play(volume=settings.get("music_volume", 50) / 100, loop=True)
try: try:
window = ControllerWindow(width=resolution[0], height=resolution[1], title='Connect the Current', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False) window = ControllerWindow(width=resolution[0], height=resolution[1], title='Connect the Current', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False)
except (FileNotFoundError, PermissionError) as e: except (FileNotFoundError, PermissionError) as e:
logging.warning(f"Controller support unavailable: {e}. Falling back to regular window.") logging.warning(f"Controller support unavailable: {e}. Falling back to regular window.")
window = arcade.Window(width=resolution[0], height=resolution[1], title='Connect the Current', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False) window = arcade.Window(width=resolution[0], height=resolution[1], title='Connect the Current', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style, visible=False)
if vsync: if vsync:
window.set_vsync(True) window.set_vsync(True)
display_mode = window.display.get_default_screen().get_mode() display_mode = window.display.get_default_screen().get_mode()
if display_mode: if display_mode:
refresh_rate = display_mode.rate refresh_rate = display_mode.rate
else: else:
refresh_rate = 60 refresh_rate = 60
window.set_update_rate(1 / refresh_rate) window.set_update_rate(1 / refresh_rate)
window.set_draw_rate(1 / refresh_rate) window.set_draw_rate(1 / refresh_rate)
elif not fps_limit == 0: elif not fps_limit == 0:
window.set_update_rate(1 / fps_limit) window.set_update_rate(1 / fps_limit)
window.set_draw_rate(1 / fps_limit) window.set_draw_rate(1 / fps_limit)
else: else:
window.set_update_rate(1 / 99999999) window.set_update_rate(1 / 99999999)
window.set_draw_rate(1 / 99999999) window.set_draw_rate(1 / 99999999)
arcade.set_background_color(menu_background_color) arcade.set_background_color(menu_background_color)
print_debug_info() print_debug_info()
main = Main() main = Main()
window.show_view(main) window.show_view(main)
# Make window visible after all setup is complete (helps prevent double window on Wayland) # Make window visible after all setup is complete (helps prevent double window on Wayland)
window.set_visible(True) window.set_visible(True)
logging.debug('Game started.') logging.debug('Game started.')
arcade.run() arcade.run()
logging.info('Exited with error code 0.') logging.info('Exited with error code 0.')

View File

@@ -1,71 +1,71 @@
import arcade.color import arcade.color
from arcade.types import Color from arcade.types import Color
from arcade.gui.widgets.buttons import UITextureButtonStyle, UIFlatButtonStyle from arcade.gui.widgets.buttons import UITextureButtonStyle, UIFlatButtonStyle
from arcade.gui.widgets.slider import UISliderStyle from arcade.gui.widgets.slider import UISliderStyle
CUSTOM_DIFFICULTY_SETTINGS = [ CUSTOM_DIFFICULTY_SETTINGS = [
["source_count", "Source Count", 1, 20], ["source_count", "Source Count", 1, 20],
["house_count", "House Count", 1, 20], ["house_count", "House Count", 1, 20],
["size", "Size", 3, 30] ["size", "Size", 3, 30]
] ]
ROTATIONS = { ROTATIONS = {
"line": ["vertical", "horizontal"], "line": ["vertical", "horizontal"],
"corner": ["right_bottom", "left_bottom", "left_top", "right_top"], "corner": ["right_bottom", "left_bottom", "left_top", "right_top"],
"t_junction": ["top_bottom_right", "left_right_bottom", "top_bottom_left", "left_right_top"], "t_junction": ["top_bottom_right", "left_right_bottom", "top_bottom_left", "left_right_top"],
"cross": ["cross"] "cross": ["cross"]
} }
NEIGHBOURS = { NEIGHBOURS = {
"vertical": {"b", "t"}, "vertical": {"b", "t"},
"horizontal": {"l", "r"}, "horizontal": {"l", "r"},
"left_bottom": {"l", "b"}, "left_bottom": {"l", "b"},
"right_bottom": {"r", "b"}, "right_bottom": {"r", "b"},
"left_top": {"l", "t"}, "left_top": {"l", "t"},
"right_top": {"r", "t"}, "right_top": {"r", "t"},
"top_bottom_right": {"t", "b", "r"}, "top_bottom_right": {"t", "b", "r"},
"top_bottom_left": {"t", "b", "l"}, "top_bottom_left": {"t", "b", "l"},
"left_right_bottom": {"l", "r", "b"}, "left_right_bottom": {"l", "r", "b"},
"left_right_top": {"l", "r", "t"}, "left_right_top": {"l", "r", "t"},
"cross": {"l", "r", "t", "b"} "cross": {"l", "r", "t", "b"}
} }
DIRECTIONS = {"t": (0, -1, "b"), "b": (0, 1, "t"), "l": (-1, 0, "r"), "r": (1, 0, "l")} DIRECTIONS = {"t": (0, -1, "b"), "b": (0, 1, "t"), "l": (-1, 0, "r"), "r": (1, 0, "l")}
menu_background_color = (30, 30, 47) menu_background_color = (30, 30, 47)
log_dir = 'logs' log_dir = 'logs'
discord_presence_id = 1435687634960777266 discord_presence_id = 1435687634960777266
button_style = {'normal': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK), 'hover': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK), button_style = {'normal': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK), 'hover': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK),
'press': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK), 'disabled': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK)} 'press': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK), 'disabled': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK)}
big_button_style = {'normal': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26), 'hover': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26), big_button_style = {'normal': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26), 'hover': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26),
'press': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26), 'disabled': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26)} 'press': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26), 'disabled': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, font_size=26)}
dropdown_style = {'normal': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(128, 128, 128)), 'hover': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(49, 154, 54)), dropdown_style = {'normal': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(128, 128, 128)), 'hover': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(49, 154, 54)),
'press': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(128, 128, 128)), 'disabled': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(128, 128, 128))} 'press': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(128, 128, 128)), 'disabled': UIFlatButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK, bg=Color(128, 128, 128))}
slider_default_style = UISliderStyle(bg=Color(128, 128, 128), unfilled_track=Color(128, 128, 128), filled_track=Color(49, 154, 54)) slider_default_style = UISliderStyle(bg=Color(128, 128, 128), unfilled_track=Color(128, 128, 128), filled_track=Color(49, 154, 54))
slider_hover_style = UISliderStyle(bg=Color(49, 154, 54), unfilled_track=Color(128, 128, 128), filled_track=Color(49, 154, 54)) slider_hover_style = UISliderStyle(bg=Color(49, 154, 54), unfilled_track=Color(128, 128, 128), filled_track=Color(49, 154, 54))
slider_style = {'normal': slider_default_style, 'hover': slider_hover_style, 'press': slider_hover_style, 'disabled': slider_default_style} slider_style = {'normal': slider_default_style, 'hover': slider_hover_style, 'press': slider_hover_style, 'disabled': slider_default_style}
settings = { settings = {
"Graphics": { "Graphics": {
"Window Mode": {"type": "option", "options": ["Windowed", "Fullscreen", "Borderless"], "config_key": "window_mode", "default": "Windowed"}, "Window Mode": {"type": "option", "options": ["Windowed", "Fullscreen", "Borderless"], "config_key": "window_mode", "default": "Windowed"},
"Resolution": {"type": "option", "options": ["1366x768", "1440x900", "1600x900", "1920x1080", "2560x1440", "3840x2160"], "config_key": "resolution"}, "Resolution": {"type": "option", "options": ["1366x768", "1440x900", "1600x900", "1920x1080", "2560x1440", "3840x2160"], "config_key": "resolution"},
"Anti-Aliasing": {"type": "option", "options": ["None", "2x MSAA", "4x MSAA", "8x MSAA", "16x MSAA"], "config_key": "anti_aliasing", "default": "4x MSAA"}, "Anti-Aliasing": {"type": "option", "options": ["None", "2x MSAA", "4x MSAA", "8x MSAA", "16x MSAA"], "config_key": "anti_aliasing", "default": "4x MSAA"},
"VSync": {"type": "bool", "config_key": "vsync", "default": True}, "VSync": {"type": "bool", "config_key": "vsync", "default": True},
"FPS Limit": {"type": "slider", "min": 0, "max": 480, "config_key": "fps_limit", "default": 60}, "FPS Limit": {"type": "slider", "min": 0, "max": 480, "config_key": "fps_limit", "default": 60},
}, },
"Sound": { "Sound": {
"Music": {"type": "bool", "config_key": "music", "default": True}, "Music": {"type": "bool", "config_key": "music", "default": True},
"SFX": {"type": "bool", "config_key": "sfx", "default": True}, "SFX": {"type": "bool", "config_key": "sfx", "default": True},
"Music Volume": {"type": "slider", "min": 0, "max": 100, "config_key": "music_volume", "default": 50}, "Music Volume": {"type": "slider", "min": 0, "max": 100, "config_key": "music_volume", "default": 50},
"SFX Volume": {"type": "slider", "min": 0, "max": 100, "config_key": "sfx_volume", "default": 50}, "SFX Volume": {"type": "slider", "min": 0, "max": 100, "config_key": "sfx_volume", "default": 50},
}, },
"Miscellaneous": { "Miscellaneous": {
"Discord RPC": {"type": "bool", "config_key": "discord_rpc", "default": True}, "Discord RPC": {"type": "bool", "config_key": "discord_rpc", "default": True},
}, },
"Credits": {} "Credits": {}
} }
settings_start_category = "Graphics" settings_start_category = "Graphics"

View File

@@ -1,46 +1,46 @@
import arcade.gui, arcade import arcade.gui, arcade
button_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button.png")) button_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button.png"))
button_hovered_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button_hovered.png")) button_hovered_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button_hovered.png"))
wire_sound_effect = arcade.Sound("assets/sound/wire.mp3") wire_sound_effect = arcade.Sound("assets/sound/wire.mp3")
TEXTURE_MAP = { TEXTURE_MAP = {
("line", "vertical", True): arcade.load_texture("assets/graphics/powered_lines/line/vertical.png"), ("line", "vertical", True): arcade.load_texture("assets/graphics/powered_lines/line/vertical.png"),
("line", "vertical", False): arcade.load_texture("assets/graphics/unpowered_lines/line/vertical.png"), ("line", "vertical", False): arcade.load_texture("assets/graphics/unpowered_lines/line/vertical.png"),
("line", "horizontal", True): arcade.load_texture("assets/graphics/powered_lines/line/horizontal.png"), ("line", "horizontal", True): arcade.load_texture("assets/graphics/powered_lines/line/horizontal.png"),
("line", "horizontal", False): arcade.load_texture("assets/graphics/unpowered_lines/line/horizontal.png"), ("line", "horizontal", False): arcade.load_texture("assets/graphics/unpowered_lines/line/horizontal.png"),
("corner", "left_bottom", True): arcade.load_texture("assets/graphics/powered_lines/corner/left_bottom.png"), ("corner", "left_bottom", True): arcade.load_texture("assets/graphics/powered_lines/corner/left_bottom.png"),
("corner", "left_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/left_bottom.png"), ("corner", "left_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/left_bottom.png"),
("corner", "left_top", True): arcade.load_texture("assets/graphics/powered_lines/corner/left_top.png"), ("corner", "left_top", True): arcade.load_texture("assets/graphics/powered_lines/corner/left_top.png"),
("corner", "left_top", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/left_top.png"), ("corner", "left_top", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/left_top.png"),
("corner", "right_bottom", True): arcade.load_texture("assets/graphics/powered_lines/corner/right_bottom.png"), ("corner", "right_bottom", True): arcade.load_texture("assets/graphics/powered_lines/corner/right_bottom.png"),
("corner", "right_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/right_bottom.png"), ("corner", "right_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/right_bottom.png"),
("corner", "right_top", True): arcade.load_texture("assets/graphics/powered_lines/corner/right_top.png"), ("corner", "right_top", True): arcade.load_texture("assets/graphics/powered_lines/corner/right_top.png"),
("corner", "right_top", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/right_top.png"), ("corner", "right_top", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/right_top.png"),
("t_junction", "left_right_bottom", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/left_right_bottom.png"), ("t_junction", "left_right_bottom", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/left_right_bottom.png"),
("t_junction", "left_right_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/left_right_bottom.png"), ("t_junction", "left_right_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/left_right_bottom.png"),
("t_junction", "left_right_top", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/left_right_top.png"), ("t_junction", "left_right_top", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/left_right_top.png"),
("t_junction", "left_right_top", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/left_right_top.png"), ("t_junction", "left_right_top", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/left_right_top.png"),
("t_junction", "top_bottom_left", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/top_bottom_left.png"), ("t_junction", "top_bottom_left", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/top_bottom_left.png"),
("t_junction", "top_bottom_left", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/top_bottom_left.png"), ("t_junction", "top_bottom_left", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/top_bottom_left.png"),
("t_junction", "top_bottom_right", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/top_bottom_right.png"), ("t_junction", "top_bottom_right", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/top_bottom_right.png"),
("t_junction", "top_bottom_right", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/top_bottom_right.png"), ("t_junction", "top_bottom_right", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/top_bottom_right.png"),
("cross", "cross", True): arcade.load_texture("assets/graphics/powered_lines/cross/cross.png"), ("cross", "cross", True): arcade.load_texture("assets/graphics/powered_lines/cross/cross.png"),
("cross", "cross", False): arcade.load_texture("assets/graphics/unpowered_lines/cross/cross.png"), ("cross", "cross", False): arcade.load_texture("assets/graphics/unpowered_lines/cross/cross.png"),
("power_source", "cross", True): arcade.load_texture("assets/graphics/power_source.png"), ("power_source", "cross", True): arcade.load_texture("assets/graphics/power_source.png"),
("house", "cross", True): arcade.load_texture("assets/graphics/powered_lines/cross/cross.png"), ("house", "cross", True): arcade.load_texture("assets/graphics/powered_lines/cross/cross.png"),
("house", "cross", False): arcade.load_texture("assets/graphics/unpowered_lines/cross/cross.png"), ("house", "cross", False): arcade.load_texture("assets/graphics/unpowered_lines/cross/cross.png"),
} }

View File

@@ -1,90 +1,90 @@
import logging, arcade, arcade.gui, sys, traceback import logging, arcade, arcade.gui, sys, traceback
from utils.constants import menu_background_color from utils.constants import menu_background_color
import pyglet.info, pyglet.event import pyglet.info, pyglet.event
def dump_platform(): def dump_platform():
import platform import platform
logging.debug(f'Platform: {platform.platform()}') logging.debug(f'Platform: {platform.platform()}')
logging.debug(f'Release: {platform.release()}') logging.debug(f'Release: {platform.release()}')
logging.debug(f'Machine: {platform.machine()}') logging.debug(f'Machine: {platform.machine()}')
logging.debug(f'Architecture: {platform.architecture()}') logging.debug(f'Architecture: {platform.architecture()}')
def dump_gl(context=None): def dump_gl(context=None):
if context is not None: if context is not None:
info = context.get_info() info = context.get_info()
else: else:
from pyglet.gl import gl_info as info from pyglet.gl import gl_info as info
logging.debug(f'gl_info.get_version(): {info.get_version()}') logging.debug(f'gl_info.get_version(): {info.get_version()}')
logging.debug(f'gl_info.get_vendor(): {info.get_vendor()}') logging.debug(f'gl_info.get_vendor(): {info.get_vendor()}')
logging.debug(f'gl_info.get_renderer(): {info.get_renderer()}') logging.debug(f'gl_info.get_renderer(): {info.get_renderer()}')
def print_debug_info(): def print_debug_info():
logging.debug('########################## DEBUG INFO ##########################') logging.debug('########################## DEBUG INFO ##########################')
logging.debug('') logging.debug('')
dump_platform() dump_platform()
dump_gl() dump_gl()
logging.debug('') logging.debug('')
logging.debug(f'Number of screens: {len(pyglet.display.get_display().get_screens())}') logging.debug(f'Number of screens: {len(pyglet.display.get_display().get_screens())}')
logging.debug('') logging.debug('')
for n, screen in enumerate(pyglet.display.get_display().get_screens()): for n, screen in enumerate(pyglet.display.get_display().get_screens()):
logging.debug(f"Screen #{n+1}:") logging.debug(f"Screen #{n+1}:")
logging.debug(f'DPI: {screen.get_dpi()}') logging.debug(f'DPI: {screen.get_dpi()}')
logging.debug(f'Scale: {screen.get_scale()}') logging.debug(f'Scale: {screen.get_scale()}')
logging.debug(f'Size: {screen.width}, {screen.height}') logging.debug(f'Size: {screen.width}, {screen.height}')
logging.debug(f'Position: {screen.x}, {screen.y}') logging.debug(f'Position: {screen.x}, {screen.y}')
logging.debug('') logging.debug('')
logging.debug('########################## DEBUG INFO ##########################') logging.debug('########################## DEBUG INFO ##########################')
logging.debug('') logging.debug('')
class ErrorView(arcade.gui.UIView): class ErrorView(arcade.gui.UIView):
def __init__(self, message, title): def __init__(self, message, title):
super().__init__() super().__init__()
self.message = message self.message = message
self.title = title self.title = title
def exit(self): def exit(self):
logging.fatal('Exited with error code 1.') logging.fatal('Exited with error code 1.')
sys.exit(1) sys.exit(1)
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.window.set_caption('Connect the Current - Error') self.window.set_caption('Connect the Current - Error')
self.window.set_mouse_visible(True) self.window.set_mouse_visible(True)
self.window.set_exclusive_mouse(False) self.window.set_exclusive_mouse(False)
arcade.set_background_color(menu_background_color) arcade.set_background_color(menu_background_color)
msgbox = arcade.gui.UIMessageBox(width=self.window.width / 2, height=self.window.height / 2, message_text=self.message, title=self.title) msgbox = arcade.gui.UIMessageBox(width=self.window.width / 2, height=self.window.height / 2, message_text=self.message, title=self.title)
msgbox.on_action = lambda _: self.exit() msgbox.on_action = lambda _: self.exit()
self.add_widget(msgbox) self.add_widget(msgbox)
def on_exception(*exc_info): def on_exception(*exc_info):
logging.error(f"Unhandled exception:\n{''.join(traceback.format_exception(exc_info[1], limit=None))}") logging.error(f"Unhandled exception:\n{''.join(traceback.format_exception(exc_info[1], limit=None))}")
def get_closest_resolution(): def get_closest_resolution():
allowed_resolutions = [(1366, 768), (1440, 900), (1600,900), (1920,1080), (2560,1440), (3840,2160)] allowed_resolutions = [(1366, 768), (1440, 900), (1600,900), (1920,1080), (2560,1440), (3840,2160)]
screen_width, screen_height = arcade.get_screens()[0].width, arcade.get_screens()[0].height screen_width, screen_height = arcade.get_screens()[0].width, arcade.get_screens()[0].height
if (screen_width, screen_height) in allowed_resolutions: if (screen_width, screen_height) in allowed_resolutions:
if not allowed_resolutions.index((screen_width, screen_height)) == 0: if not allowed_resolutions.index((screen_width, screen_height)) == 0:
closest_resolution = allowed_resolutions[allowed_resolutions.index((screen_width, screen_height))-1] closest_resolution = allowed_resolutions[allowed_resolutions.index((screen_width, screen_height))-1]
else: else:
closest_resolution = (screen_width, screen_height) closest_resolution = (screen_width, screen_height)
else: else:
target_width, target_height = screen_width // 2, screen_height // 2 target_width, target_height = screen_width // 2, screen_height // 2
closest_resolution = min( closest_resolution = min(
allowed_resolutions, allowed_resolutions,
key=lambda res: abs(res[0] - target_width) + abs(res[1] - target_height) key=lambda res: abs(res[0] - target_width) + abs(res[1] - target_height)
) )
return closest_resolution return closest_resolution
class FakePyPresence(): class FakePyPresence():
def __init__(self): def __init__(self):
... ...
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
... ...
def close(self, *args, **kwargs): def close(self, *args, **kwargs):
... ...

440
uv.lock generated
View File

@@ -1,220 +1,220 @@
version = 1 version = 1
revision = 2 revision = 2
requires-python = ">=3.11" requires-python = ">=3.11"
[[package]] [[package]]
name = "arcade" name = "arcade"
version = "3.2.0" version = "3.2.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pillow" }, { name = "pillow" },
{ name = "pyglet" }, { name = "pyglet" },
{ name = "pymunk" }, { name = "pymunk" },
{ name = "pytiled-parser" }, { name = "pytiled-parser" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e5/39/87eaffdfc50ec9d4b4573652ef8b80cca0592e5ccafb5fc5bc8612b1445d/arcade-3.2.0.tar.gz", hash = "sha256:1c2c56181560665f6542157b9ab316b9551274a9ee8468bae017ed5b8fee18fd", size = 41941030, upload_time = "2025-05-09T20:16:20.112Z" } sdist = { url = "https://files.pythonhosted.org/packages/e5/39/87eaffdfc50ec9d4b4573652ef8b80cca0592e5ccafb5fc5bc8612b1445d/arcade-3.2.0.tar.gz", hash = "sha256:1c2c56181560665f6542157b9ab316b9551274a9ee8468bae017ed5b8fee18fd", size = 41941030, upload_time = "2025-05-09T20:16:20.112Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/40/9a/ac86f5cbccfe5455a28308fcf2d7179af8d9c3087ad4eb45706c2a7b089b/arcade-3.2.0-py3-none-any.whl", hash = "sha256:7bb47cf643b43272e4300d8a5ca5f1b1e9e131b0f3f1d3fad013cb29528d3062", size = 42635264, upload_time = "2025-05-09T20:16:15.98Z" }, { url = "https://files.pythonhosted.org/packages/40/9a/ac86f5cbccfe5455a28308fcf2d7179af8d9c3087ad4eb45706c2a7b089b/arcade-3.2.0-py3-none-any.whl", hash = "sha256:7bb47cf643b43272e4300d8a5ca5f1b1e9e131b0f3f1d3fad013cb29528d3062", size = 42635264, upload_time = "2025-05-09T20:16:15.98Z" },
] ]
[[package]] [[package]]
name = "attrs" name = "attrs"
version = "25.3.0" version = "25.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload_time = "2025-03-13T11:10:22.779Z" } sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload_time = "2025-03-13T11:10:22.779Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload_time = "2025-03-13T11:10:21.14Z" }, { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload_time = "2025-03-13T11:10:21.14Z" },
] ]
[[package]] [[package]]
name = "cffi" name = "cffi"
version = "1.17.1" version = "1.17.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pycparser" }, { name = "pycparser" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload_time = "2024-09-04T20:45:21.852Z" } sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload_time = "2024-09-04T20:45:21.852Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload_time = "2024-09-04T20:43:51.124Z" }, { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload_time = "2024-09-04T20:43:51.124Z" },
{ url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload_time = "2024-09-04T20:43:52.872Z" }, { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload_time = "2024-09-04T20:43:52.872Z" },
{ url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload_time = "2024-09-04T20:43:56.123Z" }, { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload_time = "2024-09-04T20:43:56.123Z" },
{ url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload_time = "2024-09-04T20:43:57.891Z" }, { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload_time = "2024-09-04T20:43:57.891Z" },
{ url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload_time = "2024-09-04T20:44:00.18Z" }, { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload_time = "2024-09-04T20:44:00.18Z" },
{ url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload_time = "2024-09-04T20:44:01.585Z" }, { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload_time = "2024-09-04T20:44:01.585Z" },
{ url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload_time = "2024-09-04T20:44:03.467Z" }, { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload_time = "2024-09-04T20:44:03.467Z" },
{ url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload_time = "2024-09-04T20:44:05.023Z" }, { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload_time = "2024-09-04T20:44:05.023Z" },
{ url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload_time = "2024-09-04T20:44:06.444Z" }, { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload_time = "2024-09-04T20:44:06.444Z" },
{ url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload_time = "2024-09-04T20:44:08.206Z" }, { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload_time = "2024-09-04T20:44:08.206Z" },
{ url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload_time = "2024-09-04T20:44:09.481Z" }, { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload_time = "2024-09-04T20:44:09.481Z" },
{ url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload_time = "2024-09-04T20:44:10.873Z" }, { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload_time = "2024-09-04T20:44:10.873Z" },
{ url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload_time = "2024-09-04T20:44:12.232Z" }, { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload_time = "2024-09-04T20:44:12.232Z" },
{ url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload_time = "2024-09-04T20:44:13.739Z" }, { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload_time = "2024-09-04T20:44:13.739Z" },
{ url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload_time = "2024-09-04T20:44:15.231Z" }, { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload_time = "2024-09-04T20:44:15.231Z" },
{ url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload_time = "2024-09-04T20:44:17.188Z" }, { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload_time = "2024-09-04T20:44:17.188Z" },
{ url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload_time = "2024-09-04T20:44:18.688Z" }, { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload_time = "2024-09-04T20:44:18.688Z" },
{ url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload_time = "2024-09-04T20:44:20.248Z" }, { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload_time = "2024-09-04T20:44:20.248Z" },
{ url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload_time = "2024-09-04T20:44:21.673Z" }, { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload_time = "2024-09-04T20:44:21.673Z" },
{ url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload_time = "2024-09-04T20:44:23.245Z" }, { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload_time = "2024-09-04T20:44:23.245Z" },
{ url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload_time = "2024-09-04T20:44:24.757Z" }, { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload_time = "2024-09-04T20:44:24.757Z" },
{ url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload_time = "2024-09-04T20:44:26.208Z" }, { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload_time = "2024-09-04T20:44:26.208Z" },
{ url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload_time = "2024-09-04T20:44:27.578Z" }, { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload_time = "2024-09-04T20:44:27.578Z" },
{ url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload_time = "2024-09-04T20:44:28.956Z" }, { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload_time = "2024-09-04T20:44:28.956Z" },
{ url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload_time = "2024-09-04T20:44:30.289Z" }, { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload_time = "2024-09-04T20:44:30.289Z" },
{ url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload_time = "2024-09-04T20:44:32.01Z" }, { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload_time = "2024-09-04T20:44:32.01Z" },
{ url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload_time = "2024-09-04T20:44:33.606Z" }, { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload_time = "2024-09-04T20:44:33.606Z" },
{ url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload_time = "2024-09-04T20:44:35.191Z" }, { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload_time = "2024-09-04T20:44:35.191Z" },
{ url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload_time = "2024-09-04T20:44:36.743Z" }, { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload_time = "2024-09-04T20:44:36.743Z" },
{ url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload_time = "2024-09-04T20:44:38.492Z" }, { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload_time = "2024-09-04T20:44:38.492Z" },
{ url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload_time = "2024-09-04T20:44:40.046Z" }, { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload_time = "2024-09-04T20:44:40.046Z" },
{ url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload_time = "2024-09-04T20:44:41.616Z" }, { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload_time = "2024-09-04T20:44:41.616Z" },
{ url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload_time = "2024-09-04T20:44:43.733Z" }, { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload_time = "2024-09-04T20:44:43.733Z" },
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload_time = "2024-09-04T20:44:45.309Z" }, { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload_time = "2024-09-04T20:44:45.309Z" },
] ]
[[package]] [[package]]
name = "connect-the-current" name = "connect-the-current"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "arcade" }, { name = "arcade" },
{ name = "pypresence" }, { name = "pypresence" },
] ]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "arcade", specifier = "==3.2.0" }, { name = "arcade", specifier = "==3.2.0" },
{ name = "pypresence", specifier = ">=4.3.0" }, { name = "pypresence", specifier = ">=4.3.0" },
] ]
[[package]] [[package]]
name = "pillow" name = "pillow"
version = "11.0.0" version = "11.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a5/26/0d95c04c868f6bdb0c447e3ee2de5564411845e36a858cfd63766bc7b563/pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739", size = 46737780, upload_time = "2024-10-15T14:24:29.672Z" } sdist = { url = "https://files.pythonhosted.org/packages/a5/26/0d95c04c868f6bdb0c447e3ee2de5564411845e36a858cfd63766bc7b563/pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739", size = 46737780, upload_time = "2024-10-15T14:24:29.672Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/eb/f7e21b113dd48a9c97d364e0915b3988c6a0b6207652f5a92372871b7aa4/pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc", size = 3154705, upload_time = "2024-10-15T14:22:15.419Z" }, { url = "https://files.pythonhosted.org/packages/f0/eb/f7e21b113dd48a9c97d364e0915b3988c6a0b6207652f5a92372871b7aa4/pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc", size = 3154705, upload_time = "2024-10-15T14:22:15.419Z" },
{ url = "https://files.pythonhosted.org/packages/25/b3/2b54a1d541accebe6bd8b1358b34ceb2c509f51cb7dcda8687362490da5b/pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a", size = 2979222, upload_time = "2024-10-15T14:22:17.681Z" }, { url = "https://files.pythonhosted.org/packages/25/b3/2b54a1d541accebe6bd8b1358b34ceb2c509f51cb7dcda8687362490da5b/pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a", size = 2979222, upload_time = "2024-10-15T14:22:17.681Z" },
{ url = "https://files.pythonhosted.org/packages/20/12/1a41eddad8265c5c19dda8fb6c269ce15ee25e0b9f8f26286e6202df6693/pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3", size = 4190220, upload_time = "2024-10-15T14:22:19.826Z" }, { url = "https://files.pythonhosted.org/packages/20/12/1a41eddad8265c5c19dda8fb6c269ce15ee25e0b9f8f26286e6202df6693/pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3", size = 4190220, upload_time = "2024-10-15T14:22:19.826Z" },
{ url = "https://files.pythonhosted.org/packages/a9/9b/8a8c4d07d77447b7457164b861d18f5a31ae6418ef5c07f6f878fa09039a/pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5", size = 4291399, upload_time = "2024-10-15T14:22:22.129Z" }, { url = "https://files.pythonhosted.org/packages/a9/9b/8a8c4d07d77447b7457164b861d18f5a31ae6418ef5c07f6f878fa09039a/pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5", size = 4291399, upload_time = "2024-10-15T14:22:22.129Z" },
{ url = "https://files.pythonhosted.org/packages/fc/e4/130c5fab4a54d3991129800dd2801feeb4b118d7630148cd67f0e6269d4c/pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b", size = 4202709, upload_time = "2024-10-15T14:22:23.953Z" }, { url = "https://files.pythonhosted.org/packages/fc/e4/130c5fab4a54d3991129800dd2801feeb4b118d7630148cd67f0e6269d4c/pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b", size = 4202709, upload_time = "2024-10-15T14:22:23.953Z" },
{ url = "https://files.pythonhosted.org/packages/39/63/b3fc299528d7df1f678b0666002b37affe6b8751225c3d9c12cf530e73ed/pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa", size = 4372556, upload_time = "2024-10-15T14:22:25.706Z" }, { url = "https://files.pythonhosted.org/packages/39/63/b3fc299528d7df1f678b0666002b37affe6b8751225c3d9c12cf530e73ed/pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa", size = 4372556, upload_time = "2024-10-15T14:22:25.706Z" },
{ url = "https://files.pythonhosted.org/packages/c6/a6/694122c55b855b586c26c694937d36bb8d3b09c735ff41b2f315c6e66a10/pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306", size = 4287187, upload_time = "2024-10-15T14:22:27.362Z" }, { url = "https://files.pythonhosted.org/packages/c6/a6/694122c55b855b586c26c694937d36bb8d3b09c735ff41b2f315c6e66a10/pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306", size = 4287187, upload_time = "2024-10-15T14:22:27.362Z" },
{ url = "https://files.pythonhosted.org/packages/ba/a9/f9d763e2671a8acd53d29b1e284ca298bc10a595527f6be30233cdb9659d/pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9", size = 4418468, upload_time = "2024-10-15T14:22:29.093Z" }, { url = "https://files.pythonhosted.org/packages/ba/a9/f9d763e2671a8acd53d29b1e284ca298bc10a595527f6be30233cdb9659d/pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9", size = 4418468, upload_time = "2024-10-15T14:22:29.093Z" },
{ url = "https://files.pythonhosted.org/packages/6e/0e/b5cbad2621377f11313a94aeb44ca55a9639adabcaaa073597a1925f8c26/pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5", size = 2249249, upload_time = "2024-10-15T14:22:31.268Z" }, { url = "https://files.pythonhosted.org/packages/6e/0e/b5cbad2621377f11313a94aeb44ca55a9639adabcaaa073597a1925f8c26/pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5", size = 2249249, upload_time = "2024-10-15T14:22:31.268Z" },
{ url = "https://files.pythonhosted.org/packages/dc/83/1470c220a4ff06cd75fc609068f6605e567ea51df70557555c2ab6516b2c/pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291", size = 2566769, upload_time = "2024-10-15T14:22:32.974Z" }, { url = "https://files.pythonhosted.org/packages/dc/83/1470c220a4ff06cd75fc609068f6605e567ea51df70557555c2ab6516b2c/pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291", size = 2566769, upload_time = "2024-10-15T14:22:32.974Z" },
{ url = "https://files.pythonhosted.org/packages/52/98/def78c3a23acee2bcdb2e52005fb2810ed54305602ec1bfcfab2bda6f49f/pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9", size = 2254611, upload_time = "2024-10-15T14:22:35.496Z" }, { url = "https://files.pythonhosted.org/packages/52/98/def78c3a23acee2bcdb2e52005fb2810ed54305602ec1bfcfab2bda6f49f/pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9", size = 2254611, upload_time = "2024-10-15T14:22:35.496Z" },
{ url = "https://files.pythonhosted.org/packages/1c/a3/26e606ff0b2daaf120543e537311fa3ae2eb6bf061490e4fea51771540be/pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923", size = 3147642, upload_time = "2024-10-15T14:22:37.736Z" }, { url = "https://files.pythonhosted.org/packages/1c/a3/26e606ff0b2daaf120543e537311fa3ae2eb6bf061490e4fea51771540be/pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923", size = 3147642, upload_time = "2024-10-15T14:22:37.736Z" },
{ url = "https://files.pythonhosted.org/packages/4f/d5/1caabedd8863526a6cfa44ee7a833bd97f945dc1d56824d6d76e11731939/pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903", size = 2978999, upload_time = "2024-10-15T14:22:39.654Z" }, { url = "https://files.pythonhosted.org/packages/4f/d5/1caabedd8863526a6cfa44ee7a833bd97f945dc1d56824d6d76e11731939/pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903", size = 2978999, upload_time = "2024-10-15T14:22:39.654Z" },
{ url = "https://files.pythonhosted.org/packages/d9/ff/5a45000826a1aa1ac6874b3ec5a856474821a1b59d838c4f6ce2ee518fe9/pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4", size = 4196794, upload_time = "2024-10-15T14:22:41.598Z" }, { url = "https://files.pythonhosted.org/packages/d9/ff/5a45000826a1aa1ac6874b3ec5a856474821a1b59d838c4f6ce2ee518fe9/pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4", size = 4196794, upload_time = "2024-10-15T14:22:41.598Z" },
{ url = "https://files.pythonhosted.org/packages/9d/21/84c9f287d17180f26263b5f5c8fb201de0f88b1afddf8a2597a5c9fe787f/pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f", size = 4300762, upload_time = "2024-10-15T14:22:45.952Z" }, { url = "https://files.pythonhosted.org/packages/9d/21/84c9f287d17180f26263b5f5c8fb201de0f88b1afddf8a2597a5c9fe787f/pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f", size = 4300762, upload_time = "2024-10-15T14:22:45.952Z" },
{ url = "https://files.pythonhosted.org/packages/84/39/63fb87cd07cc541438b448b1fed467c4d687ad18aa786a7f8e67b255d1aa/pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9", size = 4210468, upload_time = "2024-10-15T14:22:47.789Z" }, { url = "https://files.pythonhosted.org/packages/84/39/63fb87cd07cc541438b448b1fed467c4d687ad18aa786a7f8e67b255d1aa/pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9", size = 4210468, upload_time = "2024-10-15T14:22:47.789Z" },
{ url = "https://files.pythonhosted.org/packages/7f/42/6e0f2c2d5c60f499aa29be14f860dd4539de322cd8fb84ee01553493fb4d/pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7", size = 4381824, upload_time = "2024-10-15T14:22:49.668Z" }, { url = "https://files.pythonhosted.org/packages/7f/42/6e0f2c2d5c60f499aa29be14f860dd4539de322cd8fb84ee01553493fb4d/pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7", size = 4381824, upload_time = "2024-10-15T14:22:49.668Z" },
{ url = "https://files.pythonhosted.org/packages/31/69/1ef0fb9d2f8d2d114db982b78ca4eeb9db9a29f7477821e160b8c1253f67/pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6", size = 4296436, upload_time = "2024-10-15T14:22:51.911Z" }, { url = "https://files.pythonhosted.org/packages/31/69/1ef0fb9d2f8d2d114db982b78ca4eeb9db9a29f7477821e160b8c1253f67/pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6", size = 4296436, upload_time = "2024-10-15T14:22:51.911Z" },
{ url = "https://files.pythonhosted.org/packages/44/ea/dad2818c675c44f6012289a7c4f46068c548768bc6c7f4e8c4ae5bbbc811/pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc", size = 4429714, upload_time = "2024-10-15T14:22:53.967Z" }, { url = "https://files.pythonhosted.org/packages/44/ea/dad2818c675c44f6012289a7c4f46068c548768bc6c7f4e8c4ae5bbbc811/pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc", size = 4429714, upload_time = "2024-10-15T14:22:53.967Z" },
{ url = "https://files.pythonhosted.org/packages/af/3a/da80224a6eb15bba7a0dcb2346e2b686bb9bf98378c0b4353cd88e62b171/pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6", size = 2249631, upload_time = "2024-10-15T14:22:56.404Z" }, { url = "https://files.pythonhosted.org/packages/af/3a/da80224a6eb15bba7a0dcb2346e2b686bb9bf98378c0b4353cd88e62b171/pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6", size = 2249631, upload_time = "2024-10-15T14:22:56.404Z" },
{ url = "https://files.pythonhosted.org/packages/57/97/73f756c338c1d86bb802ee88c3cab015ad7ce4b838f8a24f16b676b1ac7c/pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47", size = 2567533, upload_time = "2024-10-15T14:22:58.087Z" }, { url = "https://files.pythonhosted.org/packages/57/97/73f756c338c1d86bb802ee88c3cab015ad7ce4b838f8a24f16b676b1ac7c/pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47", size = 2567533, upload_time = "2024-10-15T14:22:58.087Z" },
{ url = "https://files.pythonhosted.org/packages/0b/30/2b61876e2722374558b871dfbfcbe4e406626d63f4f6ed92e9c8e24cac37/pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25", size = 2254890, upload_time = "2024-10-15T14:22:59.918Z" }, { url = "https://files.pythonhosted.org/packages/0b/30/2b61876e2722374558b871dfbfcbe4e406626d63f4f6ed92e9c8e24cac37/pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25", size = 2254890, upload_time = "2024-10-15T14:22:59.918Z" },
{ url = "https://files.pythonhosted.org/packages/63/24/e2e15e392d00fcf4215907465d8ec2a2f23bcec1481a8ebe4ae760459995/pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699", size = 3147300, upload_time = "2024-10-15T14:23:01.855Z" }, { url = "https://files.pythonhosted.org/packages/63/24/e2e15e392d00fcf4215907465d8ec2a2f23bcec1481a8ebe4ae760459995/pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699", size = 3147300, upload_time = "2024-10-15T14:23:01.855Z" },
{ url = "https://files.pythonhosted.org/packages/43/72/92ad4afaa2afc233dc44184adff289c2e77e8cd916b3ddb72ac69495bda3/pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38", size = 2978742, upload_time = "2024-10-15T14:23:03.749Z" }, { url = "https://files.pythonhosted.org/packages/43/72/92ad4afaa2afc233dc44184adff289c2e77e8cd916b3ddb72ac69495bda3/pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38", size = 2978742, upload_time = "2024-10-15T14:23:03.749Z" },
{ url = "https://files.pythonhosted.org/packages/9e/da/c8d69c5bc85d72a8523fe862f05ababdc52c0a755cfe3d362656bb86552b/pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2", size = 4194349, upload_time = "2024-10-15T14:23:06.055Z" }, { url = "https://files.pythonhosted.org/packages/9e/da/c8d69c5bc85d72a8523fe862f05ababdc52c0a755cfe3d362656bb86552b/pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2", size = 4194349, upload_time = "2024-10-15T14:23:06.055Z" },
{ url = "https://files.pythonhosted.org/packages/cd/e8/686d0caeed6b998351d57796496a70185376ed9c8ec7d99e1d19ad591fc6/pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2", size = 4298714, upload_time = "2024-10-15T14:23:07.919Z" }, { url = "https://files.pythonhosted.org/packages/cd/e8/686d0caeed6b998351d57796496a70185376ed9c8ec7d99e1d19ad591fc6/pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2", size = 4298714, upload_time = "2024-10-15T14:23:07.919Z" },
{ url = "https://files.pythonhosted.org/packages/ec/da/430015cec620d622f06854be67fd2f6721f52fc17fca8ac34b32e2d60739/pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527", size = 4208514, upload_time = "2024-10-15T14:23:10.19Z" }, { url = "https://files.pythonhosted.org/packages/ec/da/430015cec620d622f06854be67fd2f6721f52fc17fca8ac34b32e2d60739/pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527", size = 4208514, upload_time = "2024-10-15T14:23:10.19Z" },
{ url = "https://files.pythonhosted.org/packages/44/ae/7e4f6662a9b1cb5f92b9cc9cab8321c381ffbee309210940e57432a4063a/pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa", size = 4380055, upload_time = "2024-10-15T14:23:12.08Z" }, { url = "https://files.pythonhosted.org/packages/44/ae/7e4f6662a9b1cb5f92b9cc9cab8321c381ffbee309210940e57432a4063a/pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa", size = 4380055, upload_time = "2024-10-15T14:23:12.08Z" },
{ url = "https://files.pythonhosted.org/packages/74/d5/1a807779ac8a0eeed57f2b92a3c32ea1b696e6140c15bd42eaf908a261cd/pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f", size = 4296751, upload_time = "2024-10-15T14:23:13.836Z" }, { url = "https://files.pythonhosted.org/packages/74/d5/1a807779ac8a0eeed57f2b92a3c32ea1b696e6140c15bd42eaf908a261cd/pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f", size = 4296751, upload_time = "2024-10-15T14:23:13.836Z" },
{ url = "https://files.pythonhosted.org/packages/38/8c/5fa3385163ee7080bc13026d59656267daaaaf3c728c233d530e2c2757c8/pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb", size = 4430378, upload_time = "2024-10-15T14:23:15.735Z" }, { url = "https://files.pythonhosted.org/packages/38/8c/5fa3385163ee7080bc13026d59656267daaaaf3c728c233d530e2c2757c8/pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb", size = 4430378, upload_time = "2024-10-15T14:23:15.735Z" },
{ url = "https://files.pythonhosted.org/packages/ca/1d/ad9c14811133977ff87035bf426875b93097fb50af747793f013979facdb/pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798", size = 2249588, upload_time = "2024-10-15T14:23:17.905Z" }, { url = "https://files.pythonhosted.org/packages/ca/1d/ad9c14811133977ff87035bf426875b93097fb50af747793f013979facdb/pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798", size = 2249588, upload_time = "2024-10-15T14:23:17.905Z" },
{ url = "https://files.pythonhosted.org/packages/fb/01/3755ba287dac715e6afdb333cb1f6d69740a7475220b4637b5ce3d78cec2/pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de", size = 2567509, upload_time = "2024-10-15T14:23:19.643Z" }, { url = "https://files.pythonhosted.org/packages/fb/01/3755ba287dac715e6afdb333cb1f6d69740a7475220b4637b5ce3d78cec2/pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de", size = 2567509, upload_time = "2024-10-15T14:23:19.643Z" },
{ url = "https://files.pythonhosted.org/packages/c0/98/2c7d727079b6be1aba82d195767d35fcc2d32204c7a5820f822df5330152/pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84", size = 2254791, upload_time = "2024-10-15T14:23:21.601Z" }, { url = "https://files.pythonhosted.org/packages/c0/98/2c7d727079b6be1aba82d195767d35fcc2d32204c7a5820f822df5330152/pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84", size = 2254791, upload_time = "2024-10-15T14:23:21.601Z" },
{ url = "https://files.pythonhosted.org/packages/eb/38/998b04cc6f474e78b563716b20eecf42a2fa16a84589d23c8898e64b0ffd/pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b", size = 3150854, upload_time = "2024-10-15T14:23:23.91Z" }, { url = "https://files.pythonhosted.org/packages/eb/38/998b04cc6f474e78b563716b20eecf42a2fa16a84589d23c8898e64b0ffd/pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b", size = 3150854, upload_time = "2024-10-15T14:23:23.91Z" },
{ url = "https://files.pythonhosted.org/packages/13/8e/be23a96292113c6cb26b2aa3c8b3681ec62b44ed5c2bd0b258bd59503d3c/pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003", size = 2982369, upload_time = "2024-10-15T14:23:27.184Z" }, { url = "https://files.pythonhosted.org/packages/13/8e/be23a96292113c6cb26b2aa3c8b3681ec62b44ed5c2bd0b258bd59503d3c/pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003", size = 2982369, upload_time = "2024-10-15T14:23:27.184Z" },
{ url = "https://files.pythonhosted.org/packages/97/8a/3db4eaabb7a2ae8203cd3a332a005e4aba00067fc514aaaf3e9721be31f1/pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2", size = 4333703, upload_time = "2024-10-15T14:23:28.979Z" }, { url = "https://files.pythonhosted.org/packages/97/8a/3db4eaabb7a2ae8203cd3a332a005e4aba00067fc514aaaf3e9721be31f1/pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2", size = 4333703, upload_time = "2024-10-15T14:23:28.979Z" },
{ url = "https://files.pythonhosted.org/packages/28/ac/629ffc84ff67b9228fe87a97272ab125bbd4dc462745f35f192d37b822f1/pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a", size = 4412550, upload_time = "2024-10-15T14:23:30.846Z" }, { url = "https://files.pythonhosted.org/packages/28/ac/629ffc84ff67b9228fe87a97272ab125bbd4dc462745f35f192d37b822f1/pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a", size = 4412550, upload_time = "2024-10-15T14:23:30.846Z" },
{ url = "https://files.pythonhosted.org/packages/d6/07/a505921d36bb2df6868806eaf56ef58699c16c388e378b0dcdb6e5b2fb36/pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8", size = 4461038, upload_time = "2024-10-15T14:23:32.687Z" }, { url = "https://files.pythonhosted.org/packages/d6/07/a505921d36bb2df6868806eaf56ef58699c16c388e378b0dcdb6e5b2fb36/pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8", size = 4461038, upload_time = "2024-10-15T14:23:32.687Z" },
{ url = "https://files.pythonhosted.org/packages/d6/b9/fb620dd47fc7cc9678af8f8bd8c772034ca4977237049287e99dda360b66/pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", size = 2253197, upload_time = "2024-10-15T14:23:35.309Z" }, { url = "https://files.pythonhosted.org/packages/d6/b9/fb620dd47fc7cc9678af8f8bd8c772034ca4977237049287e99dda360b66/pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", size = 2253197, upload_time = "2024-10-15T14:23:35.309Z" },
{ url = "https://files.pythonhosted.org/packages/df/86/25dde85c06c89d7fc5db17940f07aae0a56ac69aa9ccb5eb0f09798862a8/pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", size = 2572169, upload_time = "2024-10-15T14:23:37.33Z" }, { url = "https://files.pythonhosted.org/packages/df/86/25dde85c06c89d7fc5db17940f07aae0a56ac69aa9ccb5eb0f09798862a8/pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", size = 2572169, upload_time = "2024-10-15T14:23:37.33Z" },
{ url = "https://files.pythonhosted.org/packages/51/85/9c33f2517add612e17f3381aee7c4072779130c634921a756c97bc29fb49/pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", size = 2256828, upload_time = "2024-10-15T14:23:39.826Z" }, { url = "https://files.pythonhosted.org/packages/51/85/9c33f2517add612e17f3381aee7c4072779130c634921a756c97bc29fb49/pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", size = 2256828, upload_time = "2024-10-15T14:23:39.826Z" },
] ]
[[package]] [[package]]
name = "pycparser" name = "pycparser"
version = "2.22" version = "2.22"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload_time = "2024-03-30T13:22:22.564Z" } sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload_time = "2024-03-30T13:22:22.564Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload_time = "2024-03-30T13:22:20.476Z" }, { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload_time = "2024-03-30T13:22:20.476Z" },
] ]
[[package]] [[package]]
name = "pyglet" name = "pyglet"
version = "2.1.6" version = "2.1.6"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f7/bc/0533ccb30566ee59b540d700dbbf916dafa89132a4d582d0fd1fe158243d/pyglet-2.1.6.tar.gz", hash = "sha256:18483880b1411b39692eaf7756819285797b1aaf9ef63d40eb9f9b5d01c63416", size = 6546705, upload_time = "2025-04-27T01:12:30.995Z" } sdist = { url = "https://files.pythonhosted.org/packages/f7/bc/0533ccb30566ee59b540d700dbbf916dafa89132a4d582d0fd1fe158243d/pyglet-2.1.6.tar.gz", hash = "sha256:18483880b1411b39692eaf7756819285797b1aaf9ef63d40eb9f9b5d01c63416", size = 6546705, upload_time = "2025-04-27T01:12:30.995Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/ad/e16f9b56c4a935934341e385753d0d0a2a83b7d320e52906b44f32698feb/pyglet-2.1.6-py3-none-any.whl", hash = "sha256:52ef9e75f3969b6a28bfa5c223e50ff03a05c2baa67bfe00d2a9eec4e831a7c5", size = 983998, upload_time = "2025-04-27T01:12:26.307Z" }, { url = "https://files.pythonhosted.org/packages/a3/ad/e16f9b56c4a935934341e385753d0d0a2a83b7d320e52906b44f32698feb/pyglet-2.1.6-py3-none-any.whl", hash = "sha256:52ef9e75f3969b6a28bfa5c223e50ff03a05c2baa67bfe00d2a9eec4e831a7c5", size = 983998, upload_time = "2025-04-27T01:12:26.307Z" },
] ]
[[package]] [[package]]
name = "pymunk" name = "pymunk"
version = "6.9.0" version = "6.9.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cffi" }, { name = "cffi" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ac/08/1513c868bc2a6bfa22d47acded27f5525c1db10bf1db4fdfa39160991616/pymunk-6.9.0.tar.gz", hash = "sha256:765f7c561a859a1b565bc517a47cc3992d6258e860f9174c533033c218af63c3", size = 3104088, upload_time = "2024-10-13T09:02:40.008Z" } sdist = { url = "https://files.pythonhosted.org/packages/ac/08/1513c868bc2a6bfa22d47acded27f5525c1db10bf1db4fdfa39160991616/pymunk-6.9.0.tar.gz", hash = "sha256:765f7c561a859a1b565bc517a47cc3992d6258e860f9174c533033c218af63c3", size = 3104088, upload_time = "2024-10-13T09:02:40.008Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/ba/34524aac6c57990aa9561c4a949543794e5f7128a0b01537ed061bdaed08/pymunk-6.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:536cf3ef9a3add0ea04d83a4c01fe090ff137fb591c3b6fff6e69102384ec5d5", size = 364338, upload_time = "2024-10-13T08:58:08.889Z" }, { url = "https://files.pythonhosted.org/packages/6f/ba/34524aac6c57990aa9561c4a949543794e5f7128a0b01537ed061bdaed08/pymunk-6.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:536cf3ef9a3add0ea04d83a4c01fe090ff137fb591c3b6fff6e69102384ec5d5", size = 364338, upload_time = "2024-10-13T08:58:08.889Z" },
{ url = "https://files.pythonhosted.org/packages/19/9a/0d4931e3114495c31b600a17f27d5541f2ee35883e7c693199e1ccdf1ab0/pymunk-6.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e474bb748ded01d96d6eac8e282446baef324b67e0280213b495b1f936c06e7", size = 346937, upload_time = "2024-10-13T08:58:10.604Z" }, { url = "https://files.pythonhosted.org/packages/19/9a/0d4931e3114495c31b600a17f27d5541f2ee35883e7c693199e1ccdf1ab0/pymunk-6.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0e474bb748ded01d96d6eac8e282446baef324b67e0280213b495b1f936c06e7", size = 346937, upload_time = "2024-10-13T08:58:10.604Z" },
{ url = "https://files.pythonhosted.org/packages/61/d0/acd6a6cd8266ac0333792ac3ae36558a58859ca806e0add8f5ea01627b24/pymunk-6.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f54bd14512ca5fed0e77f964b1de4e7da1a31386dbf125e33482874d69bb6537", size = 1065273, upload_time = "2024-10-13T08:58:13.012Z" }, { url = "https://files.pythonhosted.org/packages/61/d0/acd6a6cd8266ac0333792ac3ae36558a58859ca806e0add8f5ea01627b24/pymunk-6.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f54bd14512ca5fed0e77f964b1de4e7da1a31386dbf125e33482874d69bb6537", size = 1065273, upload_time = "2024-10-13T08:58:13.012Z" },
{ url = "https://files.pythonhosted.org/packages/5d/d3/2e5763d2eea69e8953782da83fe81a0235650339c22a4f8c65ecdd07cec0/pymunk-6.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc8d6fe79f77f3ed6e2f33682d355eedb6864684120b845a3501fdf2d3efdcb6", size = 988611, upload_time = "2024-10-13T08:58:15.262Z" }, { url = "https://files.pythonhosted.org/packages/5d/d3/2e5763d2eea69e8953782da83fe81a0235650339c22a4f8c65ecdd07cec0/pymunk-6.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc8d6fe79f77f3ed6e2f33682d355eedb6864684120b845a3501fdf2d3efdcb6", size = 988611, upload_time = "2024-10-13T08:58:15.262Z" },
{ url = "https://files.pythonhosted.org/packages/ac/db/ff2cfa5b87d3e60992b2264a03ffedc738de64d0107b4ce96c623f9098e7/pymunk-6.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:70ee413899672c2d7d2ffbecabee133dba49a109867b520d77c829c0d9b3fe92", size = 974971, upload_time = "2024-10-13T08:58:17.706Z" }, { url = "https://files.pythonhosted.org/packages/ac/db/ff2cfa5b87d3e60992b2264a03ffedc738de64d0107b4ce96c623f9098e7/pymunk-6.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:70ee413899672c2d7d2ffbecabee133dba49a109867b520d77c829c0d9b3fe92", size = 974971, upload_time = "2024-10-13T08:58:17.706Z" },
{ url = "https://files.pythonhosted.org/packages/ff/44/8fd8677048aa864d91915702522c70c5aaadedfd7cd95000b75d7aabeffd/pymunk-6.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d163dcba2e5814bc5f1274e0ee6ec2a7e06bed8bf0050f30f22b604634bf7dbc", size = 1037097, upload_time = "2024-10-13T08:58:20.264Z" }, { url = "https://files.pythonhosted.org/packages/ff/44/8fd8677048aa864d91915702522c70c5aaadedfd7cd95000b75d7aabeffd/pymunk-6.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d163dcba2e5814bc5f1274e0ee6ec2a7e06bed8bf0050f30f22b604634bf7dbc", size = 1037097, upload_time = "2024-10-13T08:58:20.264Z" },
{ url = "https://files.pythonhosted.org/packages/19/fc/e6b8bf53255f2012dbdf4a2b063b6c02f8c13ce13b21fdfd84dda64fea80/pymunk-6.9.0-cp311-cp311-win32.whl", hash = "sha256:5d3ae7df3d39afe5b11633496cd464b198d5c62bec69f767f3b61f9fe7f09b98", size = 315321, upload_time = "2024-10-13T08:58:22.475Z" }, { url = "https://files.pythonhosted.org/packages/19/fc/e6b8bf53255f2012dbdf4a2b063b6c02f8c13ce13b21fdfd84dda64fea80/pymunk-6.9.0-cp311-cp311-win32.whl", hash = "sha256:5d3ae7df3d39afe5b11633496cd464b198d5c62bec69f767f3b61f9fe7f09b98", size = 315321, upload_time = "2024-10-13T08:58:22.475Z" },
{ url = "https://files.pythonhosted.org/packages/bc/3c/925a0193bbcca7203f46fc531f4f0703885c102c1e2c118c8db35816aee3/pymunk-6.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:2db4797ecec3668d51bc112a37192ee1836e236bbacdf5ed12f5a994cf1bae33", size = 366711, upload_time = "2024-10-13T08:58:24.796Z" }, { url = "https://files.pythonhosted.org/packages/bc/3c/925a0193bbcca7203f46fc531f4f0703885c102c1e2c118c8db35816aee3/pymunk-6.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:2db4797ecec3668d51bc112a37192ee1836e236bbacdf5ed12f5a994cf1bae33", size = 366711, upload_time = "2024-10-13T08:58:24.796Z" },
{ url = "https://files.pythonhosted.org/packages/93/96/d8505f4e9661c0e5343db5492895b90b2ada6ec4547fdc7a2df50eb0cdf2/pymunk-6.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02bb0fbbbce2b12c18a033e2cec747e6c4b0db93d2cb9a20f45e569b571ba184", size = 364703, upload_time = "2024-10-13T08:58:27.144Z" }, { url = "https://files.pythonhosted.org/packages/93/96/d8505f4e9661c0e5343db5492895b90b2ada6ec4547fdc7a2df50eb0cdf2/pymunk-6.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02bb0fbbbce2b12c18a033e2cec747e6c4b0db93d2cb9a20f45e569b571ba184", size = 364703, upload_time = "2024-10-13T08:58:27.144Z" },
{ url = "https://files.pythonhosted.org/packages/54/3e/610a2f2b0c6c14038168f6f862148cb245aef867b01906ce18704acafe1c/pymunk-6.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6aae4f93ac686d5e2ec60b01faa1b3722a8ab630464d0c127e16462e7bef6292", size = 347056, upload_time = "2024-10-13T08:58:29.39Z" }, { url = "https://files.pythonhosted.org/packages/54/3e/610a2f2b0c6c14038168f6f862148cb245aef867b01906ce18704acafe1c/pymunk-6.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6aae4f93ac686d5e2ec60b01faa1b3722a8ab630464d0c127e16462e7bef6292", size = 347056, upload_time = "2024-10-13T08:58:29.39Z" },
{ url = "https://files.pythonhosted.org/packages/4a/dd/4e12fb3671a6c4f2c0604420f0f15b5402b05c4964bba001088a3d92e3b9/pymunk-6.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7734d13e490e84665b1f03e616270b248d5279ed34e03859267f67868f1b94c", size = 1071014, upload_time = "2024-10-13T08:58:32.274Z" }, { url = "https://files.pythonhosted.org/packages/4a/dd/4e12fb3671a6c4f2c0604420f0f15b5402b05c4964bba001088a3d92e3b9/pymunk-6.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7734d13e490e84665b1f03e616270b248d5279ed34e03859267f67868f1b94c", size = 1071014, upload_time = "2024-10-13T08:58:32.274Z" },
{ url = "https://files.pythonhosted.org/packages/91/f8/0618a9204aff896da8b2a9df44179390b178bf00b189851affd4809b1f03/pymunk-6.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b05dbfa58d366dea860f7259ca48483922a83620ab6a19effaa74e85a4251966", size = 990358, upload_time = "2024-10-13T08:58:35.295Z" }, { url = "https://files.pythonhosted.org/packages/91/f8/0618a9204aff896da8b2a9df44179390b178bf00b189851affd4809b1f03/pymunk-6.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b05dbfa58d366dea860f7259ca48483922a83620ab6a19effaa74e85a4251966", size = 990358, upload_time = "2024-10-13T08:58:35.295Z" },
{ url = "https://files.pythonhosted.org/packages/af/67/ea2ff4a26b66acad394e4f28e4e316fbe306d34909eca401baae211ca182/pymunk-6.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cb9520c52c043de4b2b1f83979f0d097929f6ff13c8a4059d9d211b98ae25887", size = 976300, upload_time = "2024-10-13T08:58:37.339Z" }, { url = "https://files.pythonhosted.org/packages/af/67/ea2ff4a26b66acad394e4f28e4e316fbe306d34909eca401baae211ca182/pymunk-6.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cb9520c52c043de4b2b1f83979f0d097929f6ff13c8a4059d9d211b98ae25887", size = 976300, upload_time = "2024-10-13T08:58:37.339Z" },
{ url = "https://files.pythonhosted.org/packages/91/d9/a69b268712dceacf227cfff74401e2292b53050383661d456605a1928a84/pymunk-6.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:da0e153d321073cd07a48380cfc1b7bd8d40bf4ee1b14a7ede33d90a69ee0452", size = 1042511, upload_time = "2024-10-13T08:58:40.044Z" }, { url = "https://files.pythonhosted.org/packages/91/d9/a69b268712dceacf227cfff74401e2292b53050383661d456605a1928a84/pymunk-6.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:da0e153d321073cd07a48380cfc1b7bd8d40bf4ee1b14a7ede33d90a69ee0452", size = 1042511, upload_time = "2024-10-13T08:58:40.044Z" },
{ url = "https://files.pythonhosted.org/packages/f0/40/21c2a08b027d99f351b75daa36f8a2e2385daba45098078d225811275ff8/pymunk-6.9.0-cp312-cp312-win32.whl", hash = "sha256:8325c9092345764876b1c3855126cb14450dc83dc5b141ff54983a7c77fbae52", size = 315339, upload_time = "2024-10-13T09:01:37.995Z" }, { url = "https://files.pythonhosted.org/packages/f0/40/21c2a08b027d99f351b75daa36f8a2e2385daba45098078d225811275ff8/pymunk-6.9.0-cp312-cp312-win32.whl", hash = "sha256:8325c9092345764876b1c3855126cb14450dc83dc5b141ff54983a7c77fbae52", size = 315339, upload_time = "2024-10-13T09:01:37.995Z" },
{ url = "https://files.pythonhosted.org/packages/78/b4/0a18c632f96924f969924cc5903689afcaf474d4c472305805dab391b247/pymunk-6.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:13246a79b599c44d174f5619596c62b656d8539797f28bdb2797c4b700c90a33", size = 366671, upload_time = "2024-10-13T09:01:39.965Z" }, { url = "https://files.pythonhosted.org/packages/78/b4/0a18c632f96924f969924cc5903689afcaf474d4c472305805dab391b247/pymunk-6.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:13246a79b599c44d174f5619596c62b656d8539797f28bdb2797c4b700c90a33", size = 366671, upload_time = "2024-10-13T09:01:39.965Z" },
{ url = "https://files.pythonhosted.org/packages/b6/5a/c76904d21f3fdb0b713b3a8056622733a0b773f7e55ef974fa4546068cbd/pymunk-6.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c59e5cf904e148dd0d35cffb7bafe146835042de9280672cafecc3a41caf7a3", size = 364703, upload_time = "2024-10-13T09:01:42.628Z" }, { url = "https://files.pythonhosted.org/packages/b6/5a/c76904d21f3fdb0b713b3a8056622733a0b773f7e55ef974fa4546068cbd/pymunk-6.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5c59e5cf904e148dd0d35cffb7bafe146835042de9280672cafecc3a41caf7a3", size = 364703, upload_time = "2024-10-13T09:01:42.628Z" },
{ url = "https://files.pythonhosted.org/packages/63/b2/378d54b79812da5312b10de272c27aa0ac621498e059aa50eb4eec33ab52/pymunk-6.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4cbc2d37f69d85fedc1097af64edc8f4c43973a13429d51004883cbb9342875e", size = 347058, upload_time = "2024-10-13T09:01:44.529Z" }, { url = "https://files.pythonhosted.org/packages/63/b2/378d54b79812da5312b10de272c27aa0ac621498e059aa50eb4eec33ab52/pymunk-6.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4cbc2d37f69d85fedc1097af64edc8f4c43973a13429d51004883cbb9342875e", size = 347058, upload_time = "2024-10-13T09:01:44.529Z" },
{ url = "https://files.pythonhosted.org/packages/ba/a8/c7ea141a1d0e3f5b08ad653f0b5a4ebc0e5854f92bc7049a2a921fbe0d65/pymunk-6.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd64ef76e9e47fda929a2961fe98759ac46b5a7b6126d1ba3e6f04493da6519b", size = 1070851, upload_time = "2024-10-13T09:01:46.638Z" }, { url = "https://files.pythonhosted.org/packages/ba/a8/c7ea141a1d0e3f5b08ad653f0b5a4ebc0e5854f92bc7049a2a921fbe0d65/pymunk-6.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd64ef76e9e47fda929a2961fe98759ac46b5a7b6126d1ba3e6f04493da6519b", size = 1070851, upload_time = "2024-10-13T09:01:46.638Z" },
{ url = "https://files.pythonhosted.org/packages/10/a2/f40bcc9be90c2af1fe8cf4ba4281385b48d9f5667f03f6834c49aba600fd/pymunk-6.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c568c7402acd5d9a55e3965565ae0a596e4603ba8a7b7b7f0952efadd0e69524", size = 990371, upload_time = "2024-10-13T09:01:49.622Z" }, { url = "https://files.pythonhosted.org/packages/10/a2/f40bcc9be90c2af1fe8cf4ba4281385b48d9f5667f03f6834c49aba600fd/pymunk-6.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c568c7402acd5d9a55e3965565ae0a596e4603ba8a7b7b7f0952efadd0e69524", size = 990371, upload_time = "2024-10-13T09:01:49.622Z" },
{ url = "https://files.pythonhosted.org/packages/01/ae/ff7fdf1c8d32ba89d1ccada39b5f7ed66e35420b8d31bdc9af6d5d20ea2f/pymunk-6.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f6cbe3d06e468be11a615d4facecc4a870bf58c1a27c365e655b5a85685ec942", size = 976294, upload_time = "2024-10-13T09:01:52.196Z" }, { url = "https://files.pythonhosted.org/packages/01/ae/ff7fdf1c8d32ba89d1ccada39b5f7ed66e35420b8d31bdc9af6d5d20ea2f/pymunk-6.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f6cbe3d06e468be11a615d4facecc4a870bf58c1a27c365e655b5a85685ec942", size = 976294, upload_time = "2024-10-13T09:01:52.196Z" },
{ url = "https://files.pythonhosted.org/packages/c6/90/64ef000011f0c930b42354f0d91a07b4bc7f70819ec5b6034b84198bf53f/pymunk-6.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:65a9a93a51dbaf1c77efa4d2425549888a1eda9f5c9cd9a5a89b7ca66310968a", size = 1042493, upload_time = "2024-10-13T09:01:55.665Z" }, { url = "https://files.pythonhosted.org/packages/c6/90/64ef000011f0c930b42354f0d91a07b4bc7f70819ec5b6034b84198bf53f/pymunk-6.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:65a9a93a51dbaf1c77efa4d2425549888a1eda9f5c9cd9a5a89b7ca66310968a", size = 1042493, upload_time = "2024-10-13T09:01:55.665Z" },
{ url = "https://files.pythonhosted.org/packages/e5/fb/6516bd5fe565ea51a88308869632dfc896ca6b05b2579b016ffa8047a8ec/pymunk-6.9.0-cp313-cp313-win32.whl", hash = "sha256:a78b37bb360e715657c76caedaf40cdaaf6dab354d497eda481a976cc5cab3d7", size = 315341, upload_time = "2024-10-13T09:01:58.049Z" }, { url = "https://files.pythonhosted.org/packages/e5/fb/6516bd5fe565ea51a88308869632dfc896ca6b05b2579b016ffa8047a8ec/pymunk-6.9.0-cp313-cp313-win32.whl", hash = "sha256:a78b37bb360e715657c76caedaf40cdaaf6dab354d497eda481a976cc5cab3d7", size = 315341, upload_time = "2024-10-13T09:01:58.049Z" },
{ url = "https://files.pythonhosted.org/packages/e0/7c/1542df7ffbff70a4523ccb02c9241c9fe4dc24c77b747e2c16fb94891156/pymunk-6.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:d6419e1531df80ff0bb6f1f8215e044f57415514386b7b212dc148919ca629ed", size = 366673, upload_time = "2024-10-13T09:01:59.733Z" }, { url = "https://files.pythonhosted.org/packages/e0/7c/1542df7ffbff70a4523ccb02c9241c9fe4dc24c77b747e2c16fb94891156/pymunk-6.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:d6419e1531df80ff0bb6f1f8215e044f57415514386b7b212dc148919ca629ed", size = 366673, upload_time = "2024-10-13T09:01:59.733Z" },
] ]
[[package]] [[package]]
name = "pypresence" name = "pypresence"
version = "4.3.0" version = "4.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f4/2e/d110f862720b5e3ba1b0b719657385fc4151929befa2c6981f48360aa480/pypresence-4.3.0.tar.gz", hash = "sha256:a6191a3af33a9667f2a4ef0185577c86b962ee70aa82643c472768a6fed1fbf3", size = 10696, upload_time = "2023-07-08T00:33:53.49Z" } sdist = { url = "https://files.pythonhosted.org/packages/f4/2e/d110f862720b5e3ba1b0b719657385fc4151929befa2c6981f48360aa480/pypresence-4.3.0.tar.gz", hash = "sha256:a6191a3af33a9667f2a4ef0185577c86b962ee70aa82643c472768a6fed1fbf3", size = 10696, upload_time = "2023-07-08T00:33:53.49Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/40/1d30b30e18f81eb71365681223971a9822a89b3d6ee5269dd2aa955bc228/pypresence-4.3.0-py2.py3-none-any.whl", hash = "sha256:af878c6d49315084f1b108aec86b31915080614d9421d6dd3a44737aba9ff13f", size = 11778, upload_time = "2023-07-08T00:33:52.018Z" }, { url = "https://files.pythonhosted.org/packages/1e/40/1d30b30e18f81eb71365681223971a9822a89b3d6ee5269dd2aa955bc228/pypresence-4.3.0-py2.py3-none-any.whl", hash = "sha256:af878c6d49315084f1b108aec86b31915080614d9421d6dd3a44737aba9ff13f", size = 11778, upload_time = "2023-07-08T00:33:52.018Z" },
] ]
[[package]] [[package]]
name = "pytiled-parser" name = "pytiled-parser"
version = "2.2.9" version = "2.2.9"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "attrs" }, { name = "attrs" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/79/62/0d8a2220ee0747522f3b73e4f38bea7c78aefdf707afb86decf26f799fc5/pytiled_parser-2.2.9.tar.gz", hash = "sha256:225269fdd37afcbcd3b76ea3e2cab6b1e742387027106055990db43fd7451ebd", size = 45958, upload_time = "2025-01-23T18:43:30.538Z" } sdist = { url = "https://files.pythonhosted.org/packages/79/62/0d8a2220ee0747522f3b73e4f38bea7c78aefdf707afb86decf26f799fc5/pytiled_parser-2.2.9.tar.gz", hash = "sha256:225269fdd37afcbcd3b76ea3e2cab6b1e742387027106055990db43fd7451ebd", size = 45958, upload_time = "2025-01-23T18:43:30.538Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/f7/6b6c51b50ed8681a31146e5e7ac325b78fe776ff48b1ec8f56d7e4995d72/pytiled_parser-2.2.9-py2.py3-none-any.whl", hash = "sha256:37f73d31950bf4d02ee3bda59f3d6123c55194dc8d8e876821dd2080af5f1f91", size = 44452, upload_time = "2025-01-23T18:43:28.207Z" }, { url = "https://files.pythonhosted.org/packages/d7/f7/6b6c51b50ed8681a31146e5e7ac325b78fe776ff48b1ec8f56d7e4995d72/pytiled_parser-2.2.9-py2.py3-none-any.whl", hash = "sha256:37f73d31950bf4d02ee3bda59f3d6123c55194dc8d8e876821dd2080af5f1f91", size = 44452, upload_time = "2025-01-23T18:43:28.207Z" },
] ]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.14.1" version = "4.14.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload_time = "2025-07-04T13:28:34.16Z" } sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload_time = "2025-07-04T13:28:34.16Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload_time = "2025-07-04T13:28:32.743Z" }, { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload_time = "2025-07-04T13:28:32.743Z" },
] ]