add fancy backgrounds (one of them)

This commit is contained in:
Yonokid
2025-05-12 03:10:39 -04:00
parent 83780a1734
commit 283a2bc04b
6 changed files with 156 additions and 45 deletions

5
.gitignore vendored
View File

@@ -1,2 +1,5 @@
Songs
__pycache__
__pycache__
.venv
.ruff_cache
scores.db

View File

@@ -1,3 +1,5 @@
import sqlite3
import pyray as ray
from libs.audio import audio
@@ -16,7 +18,29 @@ class Screens:
GAME = "GAME"
RESULT = "RESULT"
def create_song_db():
with sqlite3.connect('scores.db') as con:
cursor = con.cursor()
create_table_query = '''
CREATE TABLE IF NOT EXISTS Scores (
hash TEXT PRIMARY KEY,
en_name TEXT NOT NULL,
jp_name TEXT NOT NULL,
diff INTEGER,
score INTEGER,
good INTEGER,
ok INTEGER,
bad INTEGER,
drumroll INTEGER,
combo INTEGER
);
'''
cursor.execute(create_table_query)
con.commit()
print("Scores database created successfully")
def main():
create_song_db()
screen_width: int = get_config()["video"]["screen_width"]
screen_height: int = get_config()["video"]["screen_height"]
render_width, render_height = ray.get_render_width(), ray.get_render_height()

View File

@@ -14,7 +14,7 @@ right_don = ['J']
right_kat = ['K']
[audio]
device_type = 'ASIO'
device_type = 'WASAPI'
asio_buffer = 6
[video]

View File

@@ -86,8 +86,10 @@ class FadeAnimation(BaseAnimation):
class MoveAnimation(BaseAnimation):
def __init__(self, duration: float, total_distance: int = 0,
start_position: int = 0, delay: float = 0.0,
reverse_delay: Optional[float] = None,
ease_in: Optional[str] = None, ease_out: Optional[str] = None):
super().__init__(duration, delay)
self.reverse_delay = reverse_delay
self.total_distance = total_distance
self.start_position = start_position
self.ease_in = ease_in
@@ -100,7 +102,14 @@ class MoveAnimation(BaseAnimation):
elif elapsed_time >= self.delay + self.duration:
self.attribute = self.start_position + self.total_distance
self.is_finished = True
if self.reverse_delay is not None:
self.start_ms = current_time_ms
self.delay = self.reverse_delay
self.start_position = self.start_position + self.total_distance
self.total_distance = -(self.total_distance)
self.reverse_delay = None
else:
self.is_finished = True
else:
progress = (elapsed_time - self.delay) / self.duration
progress = self._apply_easing(progress, self.ease_in, self.ease_out)
@@ -194,6 +203,7 @@ class Animation:
duration: Length of the move in milliseconds
start_position: The coordinates of the object before the move
total_distance: The distance travelled from the start to end position
reverse_delay: If provided, move will play in reverse after this delay
delay: Time to wait before starting the move
ease_in: Control ease into the move
ease_out: Control ease out of the move

View File

@@ -1,3 +1,4 @@
import hashlib
import math
from collections import deque
from dataclasses import dataclass, field, fields
@@ -375,6 +376,10 @@ class TJAParser:
note = Balloon(note)
note.count = int(balloon[balloon_index])
balloon_index += 1
elif item == '8':
new_pixels_per_ms = play_note_list[-1].pixels_per_frame / (1000 / 60)
note.load_ms = note.hit_ms - (self.distance / new_pixels_per_ms)
note.pixels_per_frame = play_note_list[-1].pixels_per_frame
self.current_ms += increment
play_note_list.append(note)
self.get_moji(play_note_list, ms_per_measure)
@@ -389,3 +394,10 @@ class TJAParser:
draw_note_list = deque(sorted(play_note_list, key=lambda n: n.load_ms))
bar_list = deque(sorted(bar_list, key=lambda b: b.load_ms))
return play_note_list, draw_note_list, bar_list
def hash_note_data(self, notes: list):
n = hashlib.sha256()
for bar in notes:
for part in bar:
n.update(part.encode('utf-8'))
return n.hexdigest()

View File

@@ -1,6 +1,6 @@
import bisect
import math
import random
import sqlite3
from pathlib import Path
from typing import Optional
@@ -8,9 +8,11 @@ import pyray as ray
from libs.animation import Animation
from libs.audio import audio
from libs.backgrounds import Background
from libs.tja import Balloon, Drumroll, Note, TJAParser, calculate_base_score
from libs.utils import (
OutlinedText,
draw_scaled_texture,
get_config,
get_current_ms,
global_data,
@@ -36,6 +38,8 @@ class GameScreen:
self.start_delay = 1000
self.song_started = False
self.background = Background(width, height)
def load_textures(self):
self.textures = load_all_textures_from_zip(Path('Graphics/lumendata/enso_system/common.zip'))
zip_file = Path('Graphics/lumendata/enso_system/common.zip')
@@ -77,25 +81,6 @@ class GameScreen:
self.textures.update(load_all_textures_from_zip(Path('Graphics/lumendata/enso_system/base1p.zip')))
self.textures.update(load_all_textures_from_zip(Path('Graphics/lumendata/enso_system/don1p.zip')))
self.bg_fever_name = 'bg_fever_a_' + str(random.randint(1, 4)).zfill(2)
self.bg_normal_name = 'bg_nomal_a_' + str(random.randint(1, 5)).zfill(2)
self.chibi_name = 'chibi_a_' + str(random.randint(1, 14)).zfill(2)
self.dance_name = 'dance_a_' + str(random.randint(1, 22)).zfill(2)
self.footer_name = 'dodai_a_' + str(random.randint(1, 3)).zfill(2)
self.donbg_name = 'donbg_a_' + str(random.randint(1, 6)).zfill(2)
self.fever_name = 'fever_a_' + str(random.randint(1, 4)).zfill(2)
self.renda_name = 'renda_a_' + str(random.randint(1, 3)).zfill(2)
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.bg_fever_name}.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.bg_normal_name}.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.chibi_name}.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.dance_name}.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.footer_name}.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.donbg_name}_1p.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.donbg_name}_2p.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.fever_name}.zip')))
self.textures.update(load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.renda_name}.zip')))
self.result_transition_1 = load_texture_from_zip(Path('Graphics/lumendata/enso_result.zip'), 'retry_game_img00125.png')
self.result_transition_2 = load_texture_from_zip(Path('Graphics/lumendata/enso_result.zip'), 'retry_game_img00126.png')
@@ -154,6 +139,28 @@ class GameScreen:
self.end_ms = 0
return 'RESULT'
def write_score(self):
if get_config()['general']['autoplay']:
return
with sqlite3.connect('scores.db') as con:
cursor = con.cursor()
hash = self.tja.hash_note_data(self.tja.data_to_notes(self.player_1.difficulty)[0])
check_query = "SELECT score FROM Scores WHERE hash = ? LIMIT 1"
cursor.execute(check_query, (hash,))
result = cursor.fetchone()
if result is None or session_data.result_score > result[0]:
insert_query = '''
INSERT OR REPLACE INTO Scores (hash, en_name, jp_name, diff, score, good, ok, bad, drumroll, combo)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
'''
data = (hash, self.tja.title,
self.tja.title_ja, self.player_1.difficulty,
session_data.result_score, session_data.result_good,
session_data.result_ok, session_data.result_bad,
session_data.result_total_drumroll, session_data.result_max_combo)
cursor.execute(insert_query, data)
con.commit()
def update(self):
self.on_screen_start()
self.current_ms = get_current_ms() - self.start_ms
@@ -165,6 +172,8 @@ class GameScreen:
self.song_started = True
if self.movie is not None:
self.movie.update()
else:
self.background.update(get_current_ms(), self.player_1.gauge.gauge_length > self.player_1.gauge.clear_start[min(self.player_1.difficulty, 3)])
self.player_1.update(self)
if self.song_info is not None:
@@ -176,6 +185,7 @@ class GameScreen:
return self.on_screen_end()
elif len(self.player_1.play_notes) == 0:
session_data.result_score, session_data.result_good, session_data.result_ok, session_data.result_bad, session_data.result_max_combo, session_data.result_total_drumroll = self.player_1.get_result_score()
self.write_score()
session_data.result_gauge_length = self.player_1.gauge.gauge_length
if self.end_ms != 0:
if get_current_ms() >= self.end_ms + 8533.34:
@@ -184,25 +194,17 @@ class GameScreen:
else:
self.end_ms = get_current_ms()
def draw_background(self):
for i in range(0, self.width, self.textures[self.donbg_name + '_1p'][0].width):
ray.draw_texture(self.textures[self.donbg_name + '_1p'][0], i, 0, ray.WHITE)
ray.draw_texture(self.textures[self.bg_normal_name][0], 0, 360, ray.WHITE)
ray.draw_texture(self.textures[self.bg_normal_name][1], 0, 360, ray.fade(ray.WHITE, 0.25))
ray.draw_texture(self.textures[self.footer_name][0], 0, self.height - self.textures[self.footer_name][0].height + 20, ray.WHITE)
def draw(self):
if self.movie is not None:
self.movie.draw()
else:
self.draw_background()
self.background.draw()
self.player_1.draw(self)
if self.song_info is not None:
self.song_info.draw(self)
if self.result_transition is not None:
self.result_transition.draw(self.width, self.height, self.result_transition_1, self.result_transition_2)
class Player:
TIMING_GOOD = 25.0250015258789
TIMING_OK = 75.0750045776367
@@ -252,6 +254,7 @@ class Player:
self.input_log: dict[float, tuple] = dict()
self.gauge = Gauge(self.difficulty, metadata[-1][self.difficulty][0])
self.gauge_hit_effect: list[GaugeHitEffect] = []
self.autoplay_hit_side = 'L'
@@ -318,7 +321,7 @@ class Player:
if 5 <= current_note.type <= 7:
bisect.insort_left(self.current_notes_draw, current_note, key=lambda x: x.index)
try:
tail_note = next(note for note in self.draw_note_list if note.index == current_note.index+1)
tail_note = next(note for note in self.draw_note_list if note.type == 8)
bisect.insort_left(self.current_notes_draw, tail_note, key=lambda x: x.index)
self.draw_note_list.remove(tail_note)
except Exception as e:
@@ -357,7 +360,8 @@ class Player:
if self.combo > self.max_combo:
self.max_combo = self.combo
self.draw_arc_list.append(NoteArc(note_type, get_current_ms(), self.player_number))
self.draw_arc_list.append(NoteArc(note_type, get_current_ms(), self.player_number, note.type == 3 or note.type == 4) or note.type == 7)
#game_screen.background.chibis.append(game_screen.background.Chibi())
if note in self.current_notes_draw:
index = self.current_notes_draw.index(note)
@@ -365,7 +369,7 @@ class Player:
def check_drumroll(self, game_screen: GameScreen, drum_type: int):
note_type = game_screen.note_type_list[drum_type][0]
self.draw_arc_list.append(NoteArc(note_type, get_current_ms(), self.player_number))
self.draw_arc_list.append(NoteArc(note_type, get_current_ms(), self.player_number, drum_type == 3 or drum_type == 4))
self.curr_drumroll_count += 1
self.total_drumroll += 1
self.score += 100
@@ -511,7 +515,12 @@ class Player:
if self.lane_hit_effect is not None:
self.lane_hit_effect.update(get_current_ms())
self.animation_manager(self.draw_drum_hit_list)
self.animation_manager(self.draw_arc_list)
for anim in self.draw_arc_list:
anim.update(get_current_ms())
if anim.is_finished:
self.gauge_hit_effect.append(GaugeHitEffect(anim.texture, anim.is_big))
self.draw_arc_list.remove(anim)
self.animation_manager(self.gauge_hit_effect)
self.animation_manager(self.base_score_list)
self.score_counter.update(get_current_ms(), self.score)
self.autoplay_manager(game_screen)
@@ -625,6 +634,8 @@ class Player:
self.drumroll_counter.draw(game_screen)
for anim in self.draw_arc_list:
anim.draw(game_screen)
for anim in self.gauge_hit_effect:
anim.draw(game_screen)
if self.balloon_anim is not None:
self.balloon_anim.draw(game_screen)
self.score_counter.draw(game_screen)
@@ -725,9 +736,58 @@ class DrumHitEffect:
elif self.side == 'R':
ray.draw_texture(game_screen.textures['lane_obi'][17], x, y, self.color)
class GaugeHitEffect:
def __init__(self, note_texture: ray.Texture, big: bool):
self.note_texture = note_texture
self.is_big = big
self.texture_change = Animation.create_texture_change(116.67, textures=[(0, 33.33, 1), (33.33, 66.66, 2), (66.66, float('inf'), 3)])
self.circle_fadein = Animation.create_fade(133, initial_opacity=0.0, final_opacity=1.0, delay=16.67)
self.resize = Animation.create_texture_resize(233, delay=self.texture_change.duration, initial_size=0.75, final_size=1.15)
self.fade_out = Animation.create_fade(66, delay=233)
self.test = Animation.create_fade(300, delay=116.67, initial_opacity=0.0, final_opacity=1.0)
self.color = ray.fade(ray.YELLOW, self.circle_fadein.attribute)
self.is_finished = False
def update(self, current_ms):
self.texture_change.update(current_ms)
self.circle_fadein.update(current_ms)
self.fade_out.update(current_ms)
self.resize.update(current_ms)
self.test.update(current_ms)
color = ray.YELLOW
if self.circle_fadein.is_finished:
color = ray.WHITE
self.color = ray.fade(color, min(self.fade_out.attribute, self.circle_fadein.attribute))
if self.fade_out.is_finished:
self.is_finished = True
def draw(self, game_screen):
texture = game_screen.textures['onp_kiseki_don_1p'][self.texture_change.attribute]
color_map = {0.70: ray.WHITE, 0.80: ray.YELLOW, 0.90: ray.ORANGE, 1.00: ray.RED}
texture_color = ray.WHITE
for upper_bound, color in color_map.items():
lower_bound = list(color_map.keys())[list(color_map.keys()).index(upper_bound) - 1] if list(color_map.keys()).index(upper_bound) > 0 else 0.70
if lower_bound <= self.resize.attribute <= upper_bound:
texture_color = color
elif self.resize.attribute >= upper_bound:
texture_color = ray.RED
original_x = 1223
original_y = 164
source = ray.Rectangle(0, 0, texture.width, texture.height)
dest_width = texture.width * self.resize.attribute
dest_height = texture.height * self.resize.attribute
dest = ray.Rectangle(original_x, original_y, dest_width, dest_height)
origin = ray.Vector2(dest_width / 2, dest_height / 2)
ray.draw_texture_pro(texture, source, dest, origin, self.test.attribute*100, ray.fade(texture_color, self.fade_out.attribute))
ray.draw_texture(self.note_texture, 1187 - 29, 130 - 29, ray.fade(ray.WHITE, self.fade_out.attribute))
if self.is_big:
ray.draw_texture(game_screen.textures['onp_kiseki_don_1p'][20], 1187 - 29, 130 - 29, self.color)
else:
ray.draw_texture(game_screen.textures['onp_kiseki_don_1p'][4], 1187 - 29, 130 - 29, self.color)
class NoteArc:
def __init__(self, note_texture: ray.Texture, current_ms: float, player_number: int):
def __init__(self, note_texture: ray.Texture, current_ms: float, player_number: int, big: bool):
self.texture = note_texture
self.is_big = big
self.arc_points = 22
self.create_ms = current_ms
self.player_number = player_number
@@ -886,7 +946,7 @@ class BalloonAnimation:
if self.is_popped:
ray.draw_texture(game_screen.textures['action_fusen_1p'][18], 460, 130, self.color)
elif self.balloon_count >= 1:
balloon_index = (self.balloon_count - 1) * 7 // self.balloon_total
balloon_index = min(7, (self.balloon_count - 1) * 7 // self.balloon_total)
ray.draw_texture(game_screen.textures['action_fusen_1p'][balloon_index+11], 460, 130, self.color)
if self.balloon_count > 0:
ray.draw_texture(game_screen.textures['action_fusen_1p'][0], 414, 40, ray.WHITE)
@@ -1050,9 +1110,6 @@ class SongInfo:
DISPLAY_DURATION = 1666
def __init__(self, song_name: str, genre: str):
self.fade_in = Animation.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0)
self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.FADE_DURATION + self.DISPLAY_DURATION)
self.song_name = song_name
self.genre = genre
@@ -1060,6 +1117,9 @@ class SongInfo:
self.song_title = OutlinedText(
self.font, song_name, 40, ray.WHITE, ray.BLACK, outline_thickness=5
)
self.fade_in = Animation.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0)
self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION)
self.fade_fake = Animation.create_fade(0, delay=self.DISPLAY_DURATION*2 + self.FADE_DURATION)
def _load_font_for_text(self, text: str) -> ray.Font:
codepoint_count = ray.ffi.new('int *', 0)
@@ -1070,6 +1130,7 @@ class SongInfo:
def update(self, current_ms: float):
self.fade_in.update(current_ms)
self.fade_out.update(current_ms)
self.fade_fake.update(current_ms)
if not self.fade_in.is_finished:
self.song_num_fade = ray.fade(ray.WHITE, self.fade_in.attribute)
@@ -1078,12 +1139,13 @@ class SongInfo:
self.song_num_fade = ray.fade(ray.WHITE, self.fade_out.attribute)
self.song_name_fade = ray.fade(ray.WHITE, 1 - self.fade_out.attribute)
if self.fade_out.is_finished:
self._reset_animations()
if self.fade_fake.is_finished:
self._reset_animations(current_ms)
def _reset_animations(self):
def _reset_animations(self, current_ms: float):
self.fade_in = Animation.create_fade(self.FADE_DURATION, initial_opacity=0.0, final_opacity=1.0)
self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.FADE_DURATION + self.DISPLAY_DURATION)
self.fade_out = Animation.create_fade(self.FADE_DURATION, delay=self.DISPLAY_DURATION)
self.fade_fake = Animation.create_fade(0, delay=self.DISPLAY_DURATION*2 + self.FADE_DURATION)
def draw(self, game_screen: GameScreen):
song_texture_index = (global_data.songs_played % 4) + 8