mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
Compare commits
3 Commits
ac7c7abf82
...
7ef74a601b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ef74a601b | ||
|
|
102d82001f | ||
|
|
f278868a83 |
28
README.md
28
README.md
@@ -47,18 +47,30 @@ Just make sure to use `/` and not `\`!<br>
|
|||||||
Q: I'm trying to play on Mac and it can't open!<br>
|
Q: I'm trying to play on Mac and it can't open!<br>
|
||||||
A: Delete your installation and follow these commands in the terminal:<br>
|
A: Delete your installation and follow these commands in the terminal:<br>
|
||||||
```
|
```
|
||||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
# Move to the place where you want to download Pytaiko, this example will be in the desktop
|
||||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
# Make sure the directory is not saved in iCloud! This will create an issue on macOS where the executable cannot compile.
|
||||||
source $HOME/.local/bin/env
|
cd ~/Desktop
|
||||||
|
|
||||||
|
# Installing dependencies
|
||||||
|
brew install python@3.12 uv git speexdsp libsndfile
|
||||||
|
|
||||||
|
# Downloading Pytaiko project
|
||||||
git clone https://github.com/Yonokid/PyTaiko
|
git clone https://github.com/Yonokid/PyTaiko
|
||||||
cd PyTaiko
|
cd PyTaiko
|
||||||
brew install libsndfile
|
|
||||||
brew install speexdsp
|
# Compiling audio libraries
|
||||||
cd libs/audio
|
cd libs/audio
|
||||||
make
|
make
|
||||||
mv libaudio.dylib ../../
|
mv libaudio.dylib ...
|
||||||
cd ../../
|
cd ...
|
||||||
|
|
||||||
|
# Running game (for testing)
|
||||||
uv run PyTaiko.py
|
uv run PyTaiko.py
|
||||||
|
|
||||||
|
# Creating executable
|
||||||
|
|
||||||
|
uv add nuitka
|
||||||
|
uv run nuitka --mode=app --noinclude-setuptools-mode=nofollow --noinclude-IPython-mode=nofollow --assume-yes-for-downloads PyTaiko.py
|
||||||
```
|
```
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -71,7 +83,7 @@ Download the latest release for your operating system from the [releases page](h
|
|||||||
2. Run `PyTaiko.exe`
|
2. Run `PyTaiko.exe`
|
||||||
|
|
||||||
#### macOS
|
#### macOS
|
||||||
- Run with Python directly (see [Building from Source](#building-from-source))
|
- Run with Python directly or self compile (see [Building from Source](#building-from-source))
|
||||||
|
|
||||||
#### Linux
|
#### Linux
|
||||||
- Try running the compiled `PyTaiko.bin` binary
|
- Try running the compiled `PyTaiko.bin` binary
|
||||||
|
|||||||
@@ -130,10 +130,17 @@ class Background:
|
|||||||
current_milestone = min(self.max_dancers - 1, int(gauge_1p.gauge_length / (clear_threshold / self.max_dancers)))
|
current_milestone = min(self.max_dancers - 1, int(gauge_1p.gauge_length / (clear_threshold / self.max_dancers)))
|
||||||
else:
|
else:
|
||||||
current_milestone = self.max_dancers
|
current_milestone = self.max_dancers
|
||||||
|
|
||||||
if current_milestone > self.last_milestone and current_milestone <= self.max_dancers:
|
if current_milestone > self.last_milestone and current_milestone <= self.max_dancers:
|
||||||
self.dancer.add_dancer()
|
self.dancer.add_dancer()
|
||||||
self.last_milestone = current_milestone
|
self.last_milestone = current_milestone
|
||||||
logger.info(f"Dancer milestone reached: {current_milestone}/{self.max_dancers}")
|
logger.info(f"Dancer milestone reached: {current_milestone}/{self.max_dancers}")
|
||||||
|
elif current_milestone < self.last_milestone:
|
||||||
|
dancers_to_remove = self.last_milestone - current_milestone
|
||||||
|
for _ in range(dancers_to_remove):
|
||||||
|
self.dancer.remove_dancer()
|
||||||
|
self.last_milestone = current_milestone
|
||||||
|
logger.info(f"Dancer milestones lost: {current_milestone}/{self.max_dancers} (removed {dancers_to_remove})")
|
||||||
if self.bg_fever is not None and gauge_1p is not None:
|
if self.bg_fever is not None and gauge_1p is not None:
|
||||||
if not self.is_clear and gauge_1p.is_clear:
|
if not self.is_clear and gauge_1p.is_clear:
|
||||||
self.bg_fever.start()
|
self.bg_fever.start()
|
||||||
|
|||||||
@@ -162,6 +162,12 @@ class BaseDancerGroup():
|
|||||||
dancer.start()
|
dancer.start()
|
||||||
self.active_count += 1
|
self.active_count += 1
|
||||||
|
|
||||||
|
def remove_dancer(self):
|
||||||
|
if self.active_count > 1:
|
||||||
|
self.active_count -= 1
|
||||||
|
position = self.spawn_positions[self.active_count]
|
||||||
|
self.active_dancers[position] = None
|
||||||
|
|
||||||
def update(self, current_time_ms: float, bpm: float):
|
def update(self, current_time_ms: float, bpm: float):
|
||||||
for dancer in self.dancers:
|
for dancer in self.dancers:
|
||||||
dancer.update(current_time_ms, bpm)
|
dancer.update(current_time_ms, bpm)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from libs.chara_2d import Chara2D
|
|||||||
from libs.global_data import Difficulty, Modifiers, PlayerNum, global_data
|
from libs.global_data import Difficulty, Modifiers, PlayerNum, global_data
|
||||||
from libs.global_objects import Nameplate
|
from libs.global_objects import Nameplate
|
||||||
from libs.texture import tex
|
from libs.texture import tex
|
||||||
from libs.tja import Note, TJAParser
|
from libs.tja import TJAParser
|
||||||
from libs.utils import get_current_ms, global_tex
|
from libs.utils import get_current_ms, global_tex
|
||||||
from scenes.game import (
|
from scenes.game import (
|
||||||
DrumType,
|
DrumType,
|
||||||
@@ -103,6 +103,15 @@ class AIBattleGameScreen(GameScreen):
|
|||||||
self.total_notes = len(self.player_1.don_notes) + len(self.player_1.kat_notes)
|
self.total_notes = len(self.player_1.don_notes) + len(self.player_1.kat_notes)
|
||||||
logger.info(f"TJA initialized for two-player song: {song}")
|
logger.info(f"TJA initialized for two-player song: {song}")
|
||||||
|
|
||||||
|
def update_scoreboards(self):
|
||||||
|
section_notes = self.total_notes // 5
|
||||||
|
if self.player_1.good_count + self.player_1.ok_count + self.player_1.bad_count == section_notes:
|
||||||
|
self.player_2.good_percentage = self.player_1.good_count / section_notes
|
||||||
|
self.player_2.ok_percentage = self.player_1.ok_count / section_notes
|
||||||
|
logger.info(f"AI Good Percentage: {self.player_2.good_percentage}, AI OK Percentage: {self.player_2.ok_percentage}")
|
||||||
|
self.player_1.good_count, self.player_1.ok_count, self.player_1.bad_count = 0, 0, 0
|
||||||
|
self.player_2.good_count, self.player_2.ok_count, self.player_2.bad_count = 0, 0, 0
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
super(GameScreen, self).update()
|
super(GameScreen, self).update()
|
||||||
current_time = get_current_ms()
|
current_time = get_current_ms()
|
||||||
@@ -119,13 +128,7 @@ class AIBattleGameScreen(GameScreen):
|
|||||||
|
|
||||||
self.player_1.update(self.current_ms, current_time, None)
|
self.player_1.update(self.current_ms, current_time, None)
|
||||||
self.player_2.update(self.current_ms, current_time, None)
|
self.player_2.update(self.current_ms, current_time, None)
|
||||||
section_notes = self.total_notes // 5
|
self.update_scoreboards()
|
||||||
if self.player_1.good_count + self.player_1.ok_count + self.player_1.bad_count == section_notes:
|
|
||||||
self.player_2.good_percentage = self.player_1.good_count / section_notes
|
|
||||||
self.player_2.ok_percentage = self.player_1.ok_count / section_notes
|
|
||||||
logger.info(f"AI Good Percentage: {self.player_2.good_percentage}, AI OK Percentage: {self.player_2.ok_percentage}")
|
|
||||||
self.player_1.good_count, self.player_1.ok_count, self.player_1.bad_count = 0, 0, 0
|
|
||||||
self.player_2.good_count, self.player_2.ok_count, self.player_2.bad_count = 0, 0, 0
|
|
||||||
|
|
||||||
self.song_info.update(current_time)
|
self.song_info.update(current_time)
|
||||||
self.result_transition.update(current_time)
|
self.result_transition.update(current_time)
|
||||||
@@ -149,7 +152,9 @@ class AIBattleGameScreen(GameScreen):
|
|||||||
return self.global_keys()
|
return self.global_keys()
|
||||||
|
|
||||||
def update_background(self, current_time):
|
def update_background(self, current_time):
|
||||||
self.background.update(current_time, (self.player_1.good_count, self.player_1.ok_count), (self.player_2.good_count, self.player_2.ok_count))
|
if self.player_1.don_notes == self.player_2.don_notes and self.player_1.kat_notes == self.player_2.kat_notes:
|
||||||
|
self.background.update_values((self.player_1.good_count, self.player_1.ok_count), (self.player_2.good_count, self.player_2.ok_count))
|
||||||
|
self.background.update(current_time)
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
if self.movie is not None:
|
if self.movie is not None:
|
||||||
@@ -420,11 +425,13 @@ class AIBackground:
|
|||||||
self.contest_point_fade = Animation.create_fade(166, initial_opacity=0.0, final_opacity=1.0, reverse_delay=166, delay=166, loop=True)
|
self.contest_point_fade = Animation.create_fade(166, initial_opacity=0.0, final_opacity=1.0, reverse_delay=166, delay=166, loop=True)
|
||||||
self.contest_point_fade.start()
|
self.contest_point_fade.start()
|
||||||
|
|
||||||
def update(self, current_ms: float, player_judge: tuple[int, int], ai_judge: tuple[int, int]):
|
def update(self, current_ms: float):
|
||||||
self.contest_point_fade.update(current_ms)
|
self.contest_point_fade.update(current_ms)
|
||||||
|
|
||||||
|
def update_values(self, player_judge: tuple[int, int], ai_judge: tuple[int, int]):
|
||||||
player_total = (player_judge[0] * self.multipliers[self.difficulty][0]) + (player_judge[1] * self.multipliers[self.difficulty][1])
|
player_total = (player_judge[0] * self.multipliers[self.difficulty][0]) + (player_judge[1] * self.multipliers[self.difficulty][1])
|
||||||
ai_total = (ai_judge[0] * self.multipliers[self.difficulty][0]) + (ai_judge[1] * self.multipliers[self.difficulty][1])
|
ai_total = (ai_judge[0] * self.multipliers[self.difficulty][0]) + (ai_judge[1] * self.multipliers[self.difficulty][1])
|
||||||
self.contest_point = player_total - ai_total + 10
|
self.contest_point = ((player_total - ai_total) // 2) + 10
|
||||||
self.contest_point = min(max(1, self.contest_point), self.total_tiles - 1)
|
self.contest_point = min(max(1, self.contest_point), self.total_tiles - 1)
|
||||||
|
|
||||||
def unload(self):
|
def unload(self):
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import logging
|
|||||||
import pyray as ray
|
import pyray as ray
|
||||||
|
|
||||||
from libs.audio import audio
|
from libs.audio import audio
|
||||||
|
from libs.chara_2d import Chara2D
|
||||||
from libs.file_navigator import SongFile
|
from libs.file_navigator import SongFile
|
||||||
from libs.global_data import Difficulty, PlayerNum, global_data
|
from libs.global_data import Difficulty, PlayerNum, global_data
|
||||||
from libs.texture import tex
|
from libs.texture import tex
|
||||||
@@ -27,9 +28,11 @@ class AISongSelectScreen(SongSelectScreen):
|
|||||||
super().on_screen_start()
|
super().on_screen_start()
|
||||||
self.player_1 = AISongSelectPlayer(global_data.player_num, self.text_fade_in)
|
self.player_1 = AISongSelectPlayer(global_data.player_num, self.text_fade_in)
|
||||||
global_data.modifiers[global_data.player_num].subdiff = 0
|
global_data.modifiers[global_data.player_num].subdiff = 0
|
||||||
|
self.ai_chara = Chara2D(PlayerNum.AI-1)
|
||||||
|
|
||||||
def update_players(self, current_time) -> str:
|
def update_players(self, current_time) -> str:
|
||||||
self.player_1.update(current_time)
|
self.player_1.update(current_time)
|
||||||
|
self.ai_chara.update(current_time)
|
||||||
if self.text_fade_out.is_finished:
|
if self.text_fade_out.is_finished:
|
||||||
self.player_1.selected_song = True
|
self.player_1.selected_song = True
|
||||||
next_screen = "AI_GAME"
|
next_screen = "AI_GAME"
|
||||||
@@ -69,6 +72,7 @@ class AISongSelectScreen(SongSelectScreen):
|
|||||||
tex.draw_texture('global', 'song_select', fade=self.text_fade_out.attribute)
|
tex.draw_texture('global', 'song_select', fade=self.text_fade_out.attribute)
|
||||||
|
|
||||||
self.draw_players()
|
self.draw_players()
|
||||||
|
self.ai_chara.draw(x=tex.skin_config["song_select_chara_2p"].x, y=tex.skin_config["song_select_chara_2p"].y, mirror=True)
|
||||||
|
|
||||||
if self.state == State.BROWSING and self.navigator.items != []:
|
if self.state == State.BROWSING and self.navigator.items != []:
|
||||||
curr_item = self.navigator.get_current_item()
|
curr_item = self.navigator.get_current_item()
|
||||||
@@ -183,12 +187,8 @@ class AISongSelectPlayer(SongSelectPlayer):
|
|||||||
offset = 0
|
offset = 0
|
||||||
if self.subdiff_selector is not None:
|
if self.subdiff_selector is not None:
|
||||||
offset = -self.subdiff_selector.move.attribute*1.05
|
offset = -self.subdiff_selector.move.attribute*1.05
|
||||||
if self.player_num == PlayerNum.P1:
|
|
||||||
self.nameplate.draw(tex.skin_config["song_select_nameplate_1p"].x, tex.skin_config["song_select_nameplate_1p"].y)
|
self.nameplate.draw(tex.skin_config["song_select_nameplate_1p"].x, tex.skin_config["song_select_nameplate_1p"].y)
|
||||||
self.chara.draw(x=tex.skin_config["song_select_chara_1p"].x, y=tex.skin_config["song_select_chara_1p"].y + (offset*0.6))
|
self.chara.draw(x=tex.skin_config["song_select_chara_1p"].x, y=tex.skin_config["song_select_chara_1p"].y + (offset*0.6))
|
||||||
else:
|
|
||||||
self.nameplate.draw(tex.skin_config["song_select_nameplate_2p"].x, tex.skin_config["song_select_nameplate_2p"].y)
|
|
||||||
self.chara.draw(mirror=True, x=tex.skin_config["song_select_chara_2p"].x, y=tex.skin_config["song_select_chara_2p"].y + (offset*0.6))
|
|
||||||
|
|
||||||
if self.subdiff_selector is not None:
|
if self.subdiff_selector is not None:
|
||||||
self.subdiff_selector.draw()
|
self.subdiff_selector.draw()
|
||||||
|
|||||||
Reference in New Issue
Block a user