Files
PyTaiko/libs/backgrounds.py
Yonokid c1081d255b Add animation restart method and new backgrounds
This commit adds a new `restart()` method to animation classes to properly
reset their state, avoiding the need to recreate animations. It also adds
several new background variations and improves background selection.
2025-06-18 23:11:17 -04:00

313 lines
17 KiB
Python

import random
from pathlib import Path
import pyray as ray
from libs.animation import Animation
from libs.utils import load_all_textures_from_zip
class Background:
def __init__(self, screen_width: int, screen_height: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.donbg = DonBG.create(self.screen_width, self.screen_height, random.randint(6, 6), 1)
self.bg_normal = BGNormal.create(self.screen_width, self.screen_height, random.randint(1, 5))
self.bg_fever = BGFever.create(self.screen_width, self.screen_height, 4)
self.footer = Footer(self.screen_width, self.screen_height, random.randint(1, 3))
self.is_clear = False
def update(self, current_time_ms: float, is_clear: bool):
self.is_clear = is_clear
self.donbg.update(current_time_ms, is_clear)
self.bg_normal.update(current_time_ms)
self.bg_fever.update(current_time_ms)
def draw(self):
self.bg_normal.draw()
if self.is_clear:
self.bg_fever.draw()
self.footer.draw()
self.donbg.draw()
class DonBG:
@staticmethod
def create(screen_width: int, screen_height: int, index: int, player_num: int):
map = [None, DonBG1, DonBG1, DonBG1, DonBG1, DonBG1, DonBG6]
selected_obj = map[index]
return selected_obj(index, screen_width, screen_height, player_num)
class DonBGBase:
def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.player_num = player_num
self.name = 'donbg_a_' + str(index).zfill(2)
self.textures = (load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.name}_{self.player_num}p.zip')))
self.move = Animation.create_move(3000, start_position=0, total_distance=-self.textures[self.name + f'_{self.player_num}p'][0].width)
self.is_clear = False
def update(self, current_time_ms: float, is_clear: bool):
self.is_clear = is_clear
self.move.update(current_time_ms)
if self.move.is_finished:
self.move.restart()
class DonBG1(DonBGBase):
def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int):
super().__init__(index, screen_width, screen_height, player_num)
self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0)
def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear)
self.overlay_move.update(current_time_ms)
if self.overlay_move.is_finished:
self.overlay_move.restart()
def draw(self):
texture_index = 0
if self.is_clear:
texture_index = 2
top_texture = self.textures[self.name + f'_{self.player_num}p'][0 + texture_index]
for i in range(0, self.screen_width + top_texture.width, top_texture.width):
ray.draw_texture(top_texture, i + int(self.move.attribute), 0, ray.WHITE)
texture = self.textures[self.name + f'_{self.player_num}p'][1 + texture_index]
for i in range(0, self.screen_width + texture.width, texture.width):
ray.draw_texture(texture, i + int(self.move.attribute), int(self.overlay_move.attribute) - 50, ray.WHITE)
class DonBG6(DonBGBase):
def __init__(self, index: int, screen_width: int, screen_height: int, player_num: int):
super().__init__(index, screen_width, screen_height, player_num)
self.overlay_move = Animation.create_move(1000, start_position=0, total_distance=20, reverse_delay=0)
def update(self, current_time_ms: float, is_clear: bool):
super().update(current_time_ms, is_clear)
self.overlay_move.update(current_time_ms)
if self.overlay_move.is_finished:
self.overlay_move.restart()
def draw(self):
texture_index = 0
if self.is_clear:
texture_index = 3
top_texture = self.textures[self.name + f'_{self.player_num}p'][0 + texture_index]
for i in range(0, self.screen_width + top_texture.width, top_texture.width):
ray.draw_texture(top_texture, i + int(self.move.attribute), 0, ray.WHITE)
texture_flowers = self.textures[self.name + f'_{self.player_num}p'][1 + texture_index]
for i in range(0, self.screen_width, texture_flowers.width):
if i % (2 * texture_flowers.width) != 0:
ray.draw_texture(texture_flowers, i + int(self.move.attribute*3) + 100, -int(self.move.attribute*0.85)-100, ray.WHITE)
texture = self.textures[self.name + f'_{self.player_num}p'][2 + texture_index]
for i in range(0, self.screen_width + texture.width, texture.width):
ray.draw_texture(texture, i + int(self.move.attribute), int(self.overlay_move.attribute) - 50, ray.WHITE)
class BGNormal:
@staticmethod
def create(screen_width: int, screen_height: int, index: int):
map = [None, BGNormal1, BGNormal2, BGNormal3, BGNormal4, BGNormal5]
selected_obj = map[index]
return selected_obj(index, screen_width, screen_height)
class BGNormalBase:
def __init__(self, index: int, screen_width: int, screen_height: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.name = 'bg_nomal_a_' + str(index).zfill(2)
self.textures = (load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.name}.zip')))
class BGNormal1(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height)
self.flicker = Animation.create_fade(16.67*4, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
def update(self, current_time_ms: float):
self.flicker.update(current_time_ms)
if self.flicker.is_finished:
self.flicker.restart()
def draw(self):
ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][1], 0, 360, ray.fade(ray.WHITE, self.flicker.attribute))
class BGNormal2(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height)
self.flicker = Animation.create_fade(16.67*4, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
def update(self, current_time_ms: float):
self.flicker.update(current_time_ms)
if self.flicker.is_finished:
self.flicker.restart()
def draw(self):
ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][1], 0, 360, ray.fade(ray.WHITE, self.flicker.attribute))
class BGNormal3(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height)
self.flicker = Animation.create_fade(16.67*10, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
def update(self, current_time_ms):
self.flicker.update(current_time_ms)
if self.flicker.is_finished:
self.flicker.restart()
def draw(self):
src = ray.Rectangle(0, 0, self.textures[self.name][0].width, self.textures[self.name][0].height)
dest = ray.Rectangle(0, 360, self.screen_width, self.textures[self.name][0].height)
ray.draw_texture_pro(self.textures[self.name][0], src, dest, ray.Vector2(0, 0), 0, ray.WHITE)
ray.draw_texture(self.textures[self.name][3], (self.screen_width//2) - (self.textures[self.name][3].width//2), 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][6], 0, 360, ray.WHITE)
src = ray.Rectangle(0, 0, -self.textures[self.name][7].width, self.textures[self.name][7].height)
dest = ray.Rectangle((self.screen_width//2) - 170, 490, self.textures[self.name][7].width, self.textures[self.name][7].height)
ray.draw_texture_pro(self.textures[self.name][7], src, dest, ray.Vector2(0, 0), 0, ray.WHITE)
ray.draw_texture(self.textures[self.name][7], (self.screen_width//2) + 50, 490, ray.WHITE)
#Orange
color = ray.fade(ray.WHITE, self.flicker.attribute)
ray.draw_texture(self.textures[self.name][1], (self.screen_width//2) + 180, 300, color)
ray.draw_texture(self.textures[self.name][1], (self.screen_width//2) - 380, 300, color)
#Red Green Orange
ray.draw_texture(self.textures[self.name][2], (self.screen_width//2) - 220, 350, color)
ray.draw_texture(self.textures[self.name][4], (self.screen_width//2) - 100, 350, color)
ray.draw_texture(self.textures[self.name][1], (self.screen_width//2) + 10, 350, color)
#Yellow
ray.draw_texture(self.textures[self.name][5], (self.screen_width//2) - 220, 500, color)
ray.draw_texture(self.textures[self.name][5], (self.screen_width//2) + 10, 500, color)
ray.draw_texture(self.textures[self.name][9], (self.screen_width//2) - 320, 520, ray.WHITE)
ray.draw_texture(self.textures[self.name][10], 100, 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][11], self.screen_width - 100 - self.textures[self.name][11].width, 360, ray.WHITE)
class BGNormal4(BGNormalBase):
class Petal:
def __init__(self):
self.spawn_point = self.random_excluding_range()
duration = random.randint(1400, 2000)
self.move_y = Animation.create_move(duration, total_distance=360)
self.move_x = Animation.create_move(duration, total_distance=random.randint(-300, 300))
def random_excluding_range(self):
while True:
num = random.randint(0, 1280)
if num < 260 or num > 540:
return num
def update(self, current_time_ms):
self.move_x.update(current_time_ms)
self.move_y.update(current_time_ms)
def draw(self, texture):
ray.draw_texture(texture, self.spawn_point + int(self.move_x.attribute), 360+int(self.move_y.attribute), ray.fade(ray.WHITE, 0.75))
def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height)
self.flicker = Animation.create_fade(16.67*3, initial_opacity=0.5, final_opacity=0.4, reverse_delay=0)
self.turtle_move = Animation.create_move(3333*2, start_position=screen_width+112, total_distance=-(screen_width+(112*4)))
textures = ((0, 100, 3), (100, 200, 4), (200, 300, 5), (300, 400, 6), (400, 500, 7), (500, 600, 8))
self.turtle_change = Animation.create_texture_change(600, textures=textures)
self.petals = {self.Petal(), self.Petal(), self.Petal(), self.Petal(), self.Petal()}
def update(self, current_time_ms: float):
self.flicker.update(current_time_ms)
if self.flicker.is_finished:
self.flicker.restart()
self.turtle_move.update(current_time_ms)
if self.turtle_move.is_finished:
self.turtle_move.restart()
self.turtle_change.update(current_time_ms)
if self.turtle_change.is_finished:
self.turtle_change.restart()
for petal in self.petals:
petal.update(current_time_ms)
if petal.move_y.is_finished:
self.petals.remove(petal)
self.petals.add(self.Petal())
def draw(self):
ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][2], self.screen_width//2 - 20, 380, ray.WHITE)
ray.draw_texture(self.textures[self.name][self.turtle_change.attribute], int(self.turtle_move.attribute), 550, ray.WHITE)
ray.draw_texture(self.textures[self.name][9], 0, 360, ray.WHITE)
for petal in self.petals:
petal.draw(self.textures[self.name][10])
class BGNormal5(BGNormalBase):
def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height)
self.flicker = Animation.create_fade(16.67*10, initial_opacity=0.75, final_opacity=0.4, reverse_delay=0)
def update(self, current_time_ms: float):
self.flicker.update(current_time_ms)
if self.flicker.is_finished:
self.flicker.restart()
def draw(self):
ray.draw_texture(self.textures[self.name][0], 0, 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][13], -35, 340, ray.WHITE)
ray.draw_texture(self.textures[self.name][12], 103, 380, ray.WHITE)
ray.draw_texture(self.textures[self.name][11], 241, 400, ray.WHITE)
ray.draw_texture(self.textures[self.name][10], 380, 380, ray.WHITE)
ray.draw_texture(self.textures[self.name][9], 518, 340, ray.WHITE)
ray.draw_texture(self.textures[self.name][4], 657, 340, ray.WHITE)
ray.draw_texture(self.textures[self.name][5], 795, 380, ray.WHITE)
ray.draw_texture(self.textures[self.name][6], 934, 400, ray.WHITE)
ray.draw_texture(self.textures[self.name][7], 1072, 380, ray.WHITE)
ray.draw_texture(self.textures[self.name][8], 1211, 340, ray.WHITE)
color = ray.fade(ray.WHITE, self.flicker.attribute)
ray.draw_texture(self.textures[self.name][14], -35 - 10, 340 - 10, color)
ray.draw_texture(self.textures[self.name][14], 103 - 10, 380 - 10, color)
ray.draw_texture(self.textures[self.name][14], 241 - 10, 400 - 10, color)
ray.draw_texture(self.textures[self.name][14], 380 - 10, 380 - 10, color)
ray.draw_texture(self.textures[self.name][14], 518 - 10, 340 - 10, color)
ray.draw_texture(self.textures[self.name][14], 657 - 10, 340 - 10, color)
ray.draw_texture(self.textures[self.name][14], 795 - 10, 380 - 10, color)
ray.draw_texture(self.textures[self.name][14], 934 - 10, 400 - 10, color)
ray.draw_texture(self.textures[self.name][14], 1072 - 10, 380 - 10, color)
ray.draw_texture(self.textures[self.name][14], 1211 - 10, 340 - 10, color)
ray.draw_texture(self.textures[self.name][3], (self.screen_width//2) - (self.textures[self.name][3].width//2), 360+172, ray.fade(ray.WHITE, 0.75))
ray.draw_texture(self.textures[self.name][1], 50, 600, ray.fade(ray.WHITE, 0.75))
ray.draw_texture(self.textures[self.name][1], self.screen_width-50 - self.textures[self.name][2].width, 600, ray.fade(ray.WHITE, 0.75))
ray.draw_texture(self.textures[self.name][2], self.screen_width-50 - self.textures[self.name][2].width, 600, ray.WHITE)
ray.draw_texture(self.textures[self.name][2], 50, 600, ray.WHITE)
class BGFever:
@staticmethod
def create(screen_width: int, screen_height: int, index: int):
map = [None, None, None, None, BGFever4]
selected_obj = map[index]
return selected_obj(index, screen_width, screen_height)
class BGFeverBase:
def __init__(self, index: int, screen_width: int, screen_height: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.name = 'bg_fever_a_' + str(index).zfill(2)
self.textures = (load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.name}.zip')))
self.vertical_move = Animation.create_move(1300, start_position=0, total_distance=50, reverse_delay=0)
self.horizontal_move = Animation.create_move(5000, start_position=0, total_distance=self.textures[self.name][2].width)
class BGFever4(BGFeverBase):
def __init__(self, index: int, screen_width: int, screen_height: int):
super().__init__(index, screen_width, screen_height)
def update(self, current_time_ms: float):
self.vertical_move.update(current_time_ms)
if self.vertical_move.is_finished:
self.vertical_move.restart()
self.horizontal_move.update(current_time_ms)
if self.horizontal_move.is_finished:
self.horizontal_move.restart()
def draw(self):
texture = self.textures[self.name][0]
for i in range(0, self.screen_width + texture.width, texture.width):
ray.draw_texture(texture, i, 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][1], 0, 360 + 50 - int(self.vertical_move.attribute), ray.WHITE)
ray.draw_texture(self.textures[self.name][2], -int(self.horizontal_move.attribute), 360, ray.WHITE)
ray.draw_texture(self.textures[self.name][2], self.textures[self.name][2].width -int(self.horizontal_move.attribute), 360, ray.WHITE)
class Footer:
def __init__(self, screen_width: int, screen_height: int, index: int):
self.screen_width = screen_width
self.screen_height = screen_height
self.name = 'dodai_a_' + str(index).zfill(2)
self.textures = (load_all_textures_from_zip(Path(f'Graphics/lumendata/enso_original/{self.name}.zip')))
def draw(self):
ray.draw_texture(self.textures[self.name][0], 0, self.screen_height - self.textures[self.name][0].height + 20, ray.WHITE)