batframework 1.0.8a9__py3-none-any.whl → 1.0.8a10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. batFramework/__init__.py +68 -51
  2. batFramework/action.py +126 -99
  3. batFramework/actionContainer.py +53 -9
  4. batFramework/animatedSprite.py +141 -82
  5. batFramework/audioManager.py +69 -26
  6. batFramework/camera.py +259 -69
  7. batFramework/character.py +27 -0
  8. batFramework/constants.py +16 -54
  9. batFramework/cutscene.py +39 -29
  10. batFramework/cutsceneBlocks.py +36 -43
  11. batFramework/dynamicEntity.py +18 -9
  12. batFramework/easingController.py +58 -0
  13. batFramework/entity.py +48 -97
  14. batFramework/enums.py +113 -0
  15. batFramework/fontManager.py +65 -0
  16. batFramework/gui/__init__.py +10 -2
  17. batFramework/gui/button.py +9 -78
  18. batFramework/gui/clickableWidget.py +220 -0
  19. batFramework/gui/constraints/__init__.py +1 -0
  20. batFramework/gui/constraints/constraints.py +815 -0
  21. batFramework/gui/container.py +174 -32
  22. batFramework/gui/debugger.py +131 -43
  23. batFramework/gui/dialogueBox.py +99 -0
  24. batFramework/gui/draggableWidget.py +40 -0
  25. batFramework/gui/image.py +56 -20
  26. batFramework/gui/indicator.py +38 -21
  27. batFramework/gui/interactiveWidget.py +192 -13
  28. batFramework/gui/label.py +309 -74
  29. batFramework/gui/layout.py +231 -63
  30. batFramework/gui/meter.py +74 -0
  31. batFramework/gui/radioButton.py +84 -0
  32. batFramework/gui/root.py +134 -38
  33. batFramework/gui/shape.py +237 -57
  34. batFramework/gui/slider.py +240 -0
  35. batFramework/gui/style.py +10 -0
  36. batFramework/gui/styleManager.py +48 -0
  37. batFramework/gui/textInput.py +247 -0
  38. batFramework/gui/toggle.py +101 -51
  39. batFramework/gui/widget.py +358 -250
  40. batFramework/manager.py +52 -19
  41. batFramework/object.py +123 -0
  42. batFramework/particle.py +115 -0
  43. batFramework/renderGroup.py +67 -0
  44. batFramework/resourceManager.py +100 -0
  45. batFramework/scene.py +281 -123
  46. batFramework/sceneManager.py +178 -116
  47. batFramework/scrollingSprite.py +114 -0
  48. batFramework/sprite.py +51 -0
  49. batFramework/stateMachine.py +11 -8
  50. batFramework/templates/__init__.py +2 -0
  51. batFramework/templates/character.py +44 -0
  52. batFramework/templates/states.py +166 -0
  53. batFramework/tileset.py +46 -0
  54. batFramework/time.py +145 -58
  55. batFramework/transition.py +195 -124
  56. batFramework/triggerZone.py +1 -1
  57. batFramework/utils.py +112 -147
  58. batframework-1.0.8a10.dist-info/LICENCE +21 -0
  59. batframework-1.0.8a10.dist-info/METADATA +43 -0
  60. batframework-1.0.8a10.dist-info/RECORD +62 -0
  61. batFramework/debugger.py +0 -48
  62. batFramework/easing.py +0 -71
  63. batFramework/gui/constraints.py +0 -204
  64. batFramework/gui/frame.py +0 -19
  65. batFramework/particles.py +0 -77
  66. batFramework/transitionManager.py +0 -0
  67. batframework-1.0.8a9.dist-info/METADATA +0 -53
  68. batframework-1.0.8a9.dist-info/RECORD +0 -42
  69. {batframework-1.0.8a9.dist-info → batframework-1.0.8a10.dist-info}/WHEEL +0 -0
  70. {batframework-1.0.8a9.dist-info → batframework-1.0.8a10.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
1
  import batFramework as bf
2
2
  import pygame
3
+ from typing import List, Dict, Tuple, Union, Optional, Self
3
4
 
4
5
 
5
-
6
- def search_index(target, lst):
6
+ def search_index(target: int, lst: List[int]) -> int:
7
7
  cumulative_sum = 0
8
8
  for index, value in enumerate(lst):
9
9
  cumulative_sum += value
@@ -12,106 +12,165 @@ def search_index(target, lst):
12
12
  return -1
13
13
 
14
14
 
15
- class AnimState:
16
- def __init__(self, file, width, height, frame_length_list:list|int) -> None:
17
- self.frames: list[pygame.Surface] = bf.utils.img_slice(file, width, height)
18
- self.frames_flipX: list[pygame.Surface] = bf.utils.img_slice(
19
- file, width, height, True
15
+ class Animation:
16
+ def __init__(
17
+ self,
18
+ name: str
19
+ ) -> None:
20
+ self.name = name
21
+ self.frames: list[pygame.Surface] = []
22
+ self.frames_flipX : list[pygame.Surface] = []
23
+ self.duration_list = []
24
+ self.duration_list_length = 1
25
+
26
+ def from_surface(self,surface:pygame.Surface,size : Tuple[int,int])->Self:
27
+ self.frames : List[pygame.Surface] = list(bf.utils.split_surface(surface, size).values())
28
+ self.frames_flipX : List[pygame.Surface] = list(bf.utils.split_surface(surface, size,func=lambda s : pygame.transform.flip(s,True,False)).values())
29
+ return self
30
+
31
+ def __repr__(self):
32
+ return f"Animation({self.name})"
33
+
34
+ def counter_to_frame(self, counter: Union[float, int]) -> int:
35
+ if not self.frames :
36
+ raise ValueError("Animation has no frames")
37
+ return search_index(
38
+ int(counter % self.duration_list_length), self.duration_list
20
39
  )
21
40
 
22
- self.frame_length_list = []
23
- self.ffl_length = 0
24
- self.set_frame_length_list(frame_length_list)
25
-
26
- def get_frame_index(self, counter:float|int):
27
- return search_index(int(counter % self.ffl_length), self.frame_length_list)
28
-
29
- def get_frame(self, counter, flip):
30
- i = self.get_frame_index(counter)
41
+ def get_frame(self, counter: Union[float, int], flip: bool) -> pygame.Surface:
42
+ i = self.counter_to_frame(counter)
31
43
  return self.frames_flipX[i] if flip else self.frames[i]
32
44
 
33
- def set_frame_length_list(self,frame_length_list:list[int]|int):
34
- if isinstance(frame_length_list,int):
35
- frame_length_list = [frame_length_list] * len(self.frames)
36
- if len(frame_length_list) != len(self.frames) :
37
- raise ValueError("frame_length_list should have values for all frames")
38
- self.frame_length_list = frame_length_list
39
- self.ffl_length = sum(self.frame_length_list)
40
-
41
-
42
- class AnimatedSprite(bf.DynamicEntity):
43
- def __init__(self, size=None) -> None:
45
+ def set_duration_list(self, duration_list: Union[List[int], int]) -> Self:
46
+ if isinstance(duration_list, int):
47
+ duration_list = [duration_list] * len(self.frames)
48
+ if len(duration_list) != len(self.frames):
49
+ raise ValueError("duration_list should have values for all frames")
50
+ self.duration_list = duration_list
51
+ self.duration_list_length = sum(self.duration_list)
52
+ return self
53
+
54
+ class AnimatedSprite(bf.Entity):
55
+ def __init__(self, size: Optional[Tuple[int, int]] = None) -> None:
44
56
  super().__init__(size, no_surface=True)
45
- self.float_counter = 0
46
- self.animStates: dict[str, AnimState] = {}
47
- self.current_animState :str = ""
48
- self.flipX = False
49
- self._locked = False
50
-
51
- def set_counter(self,value:float):
57
+ self.float_counter: float = 0
58
+ self.animations: Dict[str, Animation] = {}
59
+ self.current_state: Optional[Animation] = None
60
+ self.flipX: bool = False
61
+ self._locked: bool = False
62
+ self._paused: bool = False
63
+ self.animation_end_callback = None
64
+ self.transition_end_animation = None
65
+
66
+ def set_animation_end_callback(self,callback)->Self:
67
+ self.animation_end_callback = callback
68
+ return self
69
+
70
+ @property
71
+ def paused(self) -> bool:
72
+ return self._paused
73
+
74
+ @paused.setter
75
+ def paused(self, value: bool) -> None:
76
+ self._paused = value
77
+
78
+ def toggle_pause(self) -> None:
79
+ self.paused = not self.paused
80
+
81
+ def set_counter(self, value: float) -> None:
52
82
  self.float_counter = value
53
-
54
83
 
55
- def lock_animState(self):
84
+ def set_frame(self, frame_index: int) -> None:
85
+ if not self.current_state:
86
+ return
87
+ self.set_counter(sum(self.current_state.duration_list[:frame_index]))
88
+
89
+ def lock(self) -> None:
56
90
  self._locked = True
57
91
 
58
- def unlock_animState(self):
92
+ def unlock(self) -> None:
59
93
  self._locked = False
60
94
 
61
- def set_flipX(self, value):
95
+ def set_flipX(self, value: bool) -> None:
62
96
  self.flipX = value
63
97
 
64
- def remove_animState(self, name:str):
65
- if not name in self.animStates :return
66
- self.animStates.pop(name)
67
- if self.current_animState == name : self.current_animState = list(self.animStates.keys())[0] if self.animStates else ""
68
-
69
- def add_animState(
70
- self, name: str, file: str, size: tuple[int, int], frame_length_list: list[int]
71
- ):
72
- if name in self.animStates:
73
- return
74
- self.animStates[name] = AnimState(file, *size, frame_length_list)
75
- if len(self.animStates) == 1 : self.set_animState(name)
98
+ def remove_animation(self, name: str) -> bool:
99
+ if name not in self.animations:
100
+ return False
101
+ self.animations.pop(name)
102
+ if self.current_state and self.current_state.name == name:
103
+ self.current_state = (
104
+ list(self.animations.values())[0] if self.animations else None
105
+ )
106
+ return True
76
107
 
77
- def set_animState(self, state:str, reset_counter=True, lock=False):
78
- if state not in self.animStates or self._locked:
108
+ def add_animation(
109
+ self,
110
+ animation:Animation
111
+ ) -> bool:
112
+ if animation.name in self.animations:
79
113
  return False
80
- self.current_animState = state
81
- self.rect = (
82
- self.animStates[self.current_animState]
83
- .frames[0]
84
- .get_frect(center=self.rect.center)
85
- )
86
- if reset_counter or self.float_counter > sum(
87
- self.get_state().frame_length_list
88
- ):
89
- self.float_counter = 0
90
- if lock:
91
- self.lock_animState()
114
+ self.animations[animation.name] = animation
92
115
  return True
93
116
 
94
- def get_state(self):
95
- return self.animStates.get(self.current_animState,None)
117
+ def set_animation(
118
+ self, state: str, reset_counter: bool = True, lock: bool = False
119
+ ) -> bool:
120
+ if state not in self.animations or self._locked:
121
+ return False
122
+
123
+ animation = self.animations[state]
124
+ self.current_state = animation
96
125
 
97
- def get_frame_index(self):
98
- return self.animStates[self.current_animState].get_frame_index(
99
- self.float_counter
100
- )
126
+ if self.current_state.frames:
127
+ self.rect = self.current_state.frames[0].get_frect(center=self.rect.center)
101
128
 
102
- def update(self, dt: float):
103
- if not self.animStates : return
104
- self.float_counter += 60 * dt
105
- if self.float_counter > self.get_state().ffl_length:
129
+ if reset_counter or (self.float_counter > animation.duration_list_length):
106
130
  self.float_counter = 0
107
- self.do_update(dt)
108
131
 
109
- def draw(self, camera: bf.Camera) -> bool:
110
- if not self.visible or not camera.intersects(self.rect) or not self.animStates:
111
- return False
112
- # pygame.draw.rect(camera.surface,"purple",camera.transpose(self.rect).move(2,2))
132
+ if lock:
133
+ self.lock()
134
+
135
+ if self.transition_end_animation is not None:
136
+ self.transition_end_animation = None
137
+ return True
138
+
139
+ def transition_to_animation(self,transition:str,animation:str)->Self:
140
+ self.set_animation(transition)
141
+ self.transition_end_animation = animation
142
+ return self
143
+
144
+ def get_current_animation(self) -> Optional[Animation]:
145
+ return self.current_state
146
+
147
+ def update(self, dt: float) -> None:
148
+ s = self.get_current_animation()
149
+ if self.animations and s is not None:
150
+ if not self.paused:
151
+ self.float_counter += 60 * dt
152
+ if self.float_counter > s.duration_list_length:
153
+ if self.transition_end_animation is not None:
154
+ # print(f"{self.transition_end_animation=}, {self.get_current_animation()=}")
155
+ self.set_animation(self.transition_end_animation)
156
+ if self.animation_end_callback is not None:
157
+ self.animation_end_callback()
158
+ self.float_counter = 0
159
+ super().update(dt)
160
+
161
+
162
+ def get_current_frame(self)->pygame.Surface:
163
+ return self.current_state.get_frame(self.float_counter, self.flipX)
164
+
165
+ def draw(self, camera: bf.Camera) -> None:
166
+ if (
167
+ not self.visible
168
+ or not camera.rect.colliderect(self.rect)
169
+ or not self.current_state
170
+ ):
171
+ return
113
172
  camera.surface.blit(
114
- self.get_state().get_frame(self.float_counter, self.flipX),
115
- camera.transpose(self.rect),
173
+ self.get_current_frame(),
174
+ camera.world_to_screen(self.rect),
116
175
  )
117
- return True
176
+ return
@@ -5,24 +5,25 @@ pygame.mixer.init()
5
5
 
6
6
 
7
7
  class AudioManager(metaclass=bf.Singleton):
8
- def __init__(self):
9
- self.sounds: dict[str : dict[str, pygame.mixer.Sound, bool]] = {}
10
- self.musics: dict[str:str] = {}
11
- self.current_music = None
12
- self.music_volume = 1
13
- self.sound_volume = 1
8
+ def __init__(self) -> None:
9
+ self.sounds: dict = {}
10
+ self.musics: dict = {}
11
+ self.current_music: str | None = None
12
+ self.music_volume: float = 1
13
+ self.sound_volume: float = 1
14
14
  pygame.mixer_music.set_endevent(bf.const.MUSIC_END_EVENT)
15
15
 
16
- def free_sounds(self, force=False):
16
+ def free_sounds(self, force: bool = False):
17
17
  if force:
18
18
  self.sounds = {}
19
19
  return
20
- to_remove = []
21
- for name, data in self.sounds.items():
22
- if not data["persistent"]:
23
- to_remove.append(name)
20
+ self.sounds: dict = {
21
+ key: value for key, value in self.sounds.items() if value["persistent"]
22
+ }
24
23
 
25
- _ = [self.sounds.pop(i) for i in to_remove]
24
+ def free_music(self):
25
+ if self.current_music:
26
+ pygame.mixer.music.unload(self.current_music)
26
27
 
27
28
  def set_sound_volume(self, volume: float):
28
29
  self.sound_volume = volume
@@ -31,13 +32,19 @@ class AudioManager(metaclass=bf.Singleton):
31
32
  self.music_volume = volume
32
33
  pygame.mixer_music.set_volume(volume)
33
34
 
34
- def has_sound(self, name):
35
+ def get_music_volume(self) -> float:
36
+ return self.music_volume
37
+
38
+ def get_sound_volume(self) -> float:
39
+ return self.sound_volume
40
+
41
+ def has_sound(self, name: str):
35
42
  return name in self.sounds
36
43
 
37
44
  def load_sound(self, name, path, persistent=False) -> pygame.mixer.Sound:
38
45
  if name in self.sounds:
39
46
  return self.sounds[name]["sound"]
40
- path = bf.utils.get_path(path)
47
+ path = bf.ResourceManager().get_path(path)
41
48
  self.sounds[name] = {
42
49
  "path": path,
43
50
  "sound": pygame.mixer.Sound(path),
@@ -45,24 +52,57 @@ class AudioManager(metaclass=bf.Singleton):
45
52
  }
46
53
  return self.sounds[name]["sound"]
47
54
 
48
- def play_sound(self, name, volume=1):
49
- self.sounds[name]["sound"].set_volume(volume * self.sound_volume)
50
- self.sounds[name]["sound"].play()
51
-
52
- def stop_sound(self, name):
53
- if name in self.sounds:
55
+ def load_sounds(self, sound_data_list: list[tuple[str, str, bool]]) -> None:
56
+ for data in sound_data_list:
57
+ self.load_sound(*data)
58
+ return
59
+
60
+ def play_sound(self, name, volume=1) -> bool:
61
+ """
62
+ Play the sound file with the given name.
63
+ returns True if the sound was played
64
+
65
+ """
66
+ try:
67
+ self.sounds[name]["sound"].set_volume(volume * self.sound_volume)
68
+ self.sounds[name]["sound"].play()
69
+ return True
70
+ except KeyError:
71
+ print(f"Sound '{name}' not loaded in AudioManager.")
72
+ return False
73
+
74
+ def stop_sound(self, name) -> bool:
75
+ try:
54
76
  self.sounds[name]["sound"].stop()
77
+ return True
78
+ except KeyError:
79
+ return False
80
+ print(f"Sound '{name}' not loaded in AudioManager.")
55
81
 
56
82
  def load_music(self, name, path):
57
- self.musics[name] = bf.utils.get_path(path)
58
-
59
- def play_music(self, name, loop=0, fade=500):
60
- if name in self.musics:
83
+ self.musics[name] = bf.ResourceManager().get_path(path)
84
+ return
85
+
86
+ def load_musics(self, music_data_list: list[tuple[str, str]]):
87
+ for data in music_data_list:
88
+ self.load_music(*data)
89
+ return
90
+
91
+ def play_music(self, name, loop=0, fade=500) -> bool:
92
+ """
93
+ Play the sound file with the given 'name'.
94
+ Fades with the given 'fade' time in ms.
95
+ Music will loop 'loop' times (indefinitely if -1).
96
+ returns True if the sound was played
97
+ """
98
+ try:
61
99
  pygame.mixer_music.load(self.musics[name])
62
100
  pygame.mixer_music.play(loop, fade_ms=fade)
63
101
  self.current_music = name
64
- else:
65
- print(f"Music '{name}' not found in AudioManager.")
102
+ return True
103
+ except KeyError:
104
+ return False
105
+ # print(f"Music '{name}' not loaded in AudioManager.")
66
106
 
67
107
  def stop_music(self):
68
108
  if not self.current_music:
@@ -83,3 +123,6 @@ class AudioManager(metaclass=bf.Singleton):
83
123
  if not self.current_music:
84
124
  return
85
125
  pygame.mixer_music.unpause()
126
+
127
+ def get_current_music(self) -> str | None:
128
+ return self.current_music