batframework 1.0.8a3__py3-none-any.whl → 1.0.8a6__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 (44) hide show
  1. batFramework/__init__.py +16 -2
  2. batFramework/animatedSprite.py +94 -85
  3. batFramework/audioManager.py +2 -2
  4. batFramework/character.py +27 -0
  5. batFramework/cutscene.py +5 -2
  6. batFramework/cutsceneBlocks.py +3 -5
  7. batFramework/dynamicEntity.py +11 -4
  8. batFramework/enums.py +2 -2
  9. batFramework/fontManager.py +2 -2
  10. batFramework/gui/clickableWidget.py +10 -9
  11. batFramework/gui/constraints/constraints.py +282 -57
  12. batFramework/gui/image.py +14 -14
  13. batFramework/gui/interactiveWidget.py +16 -1
  14. batFramework/gui/label.py +58 -37
  15. batFramework/gui/layout.py +23 -14
  16. batFramework/gui/meter.py +10 -7
  17. batFramework/gui/radioButton.py +1 -1
  18. batFramework/gui/root.py +7 -1
  19. batFramework/gui/shape.py +21 -37
  20. batFramework/gui/slider.py +52 -58
  21. batFramework/gui/textInput.py +161 -51
  22. batFramework/gui/toggle.py +27 -41
  23. batFramework/gui/widget.py +68 -35
  24. batFramework/manager.py +17 -5
  25. batFramework/object.py +17 -8
  26. batFramework/particle.py +22 -8
  27. batFramework/resourceManager.py +18 -2
  28. batFramework/scene.py +50 -20
  29. batFramework/sceneManager.py +52 -28
  30. batFramework/scrollingSprite.py +7 -8
  31. batFramework/stateMachine.py +9 -6
  32. batFramework/templates/__init__.py +2 -0
  33. batFramework/templates/character.py +44 -0
  34. batFramework/templates/states.py +166 -0
  35. batFramework/time.py +54 -28
  36. batFramework/transition.py +25 -12
  37. batFramework/triggerZone.py +1 -1
  38. batFramework/utils.py +92 -2
  39. {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/METADATA +3 -15
  40. batframework-1.0.8a6.dist-info/RECORD +62 -0
  41. {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/WHEEL +1 -1
  42. batframework-1.0.8a3.dist-info/RECORD +0 -58
  43. {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/LICENCE +0 -0
  44. {batframework-1.0.8a3.dist-info → batframework-1.0.8a6.dist-info}/top_level.txt +0 -0
batFramework/__init__.py CHANGED
@@ -8,7 +8,7 @@ from .resourceManager import ResourceManager
8
8
  from .fontManager import FontManager
9
9
  from .utils import Utils as utils
10
10
  from .tileset import Tileset
11
- from .time import TimeManager, Timer
11
+ from .time import *
12
12
  from .easingController import EasingController
13
13
  from .cutscene import Cutscene, CutsceneManager
14
14
  from .cutsceneBlocks import *
@@ -24,12 +24,15 @@ from .dynamicEntity import DynamicEntity
24
24
  from .sprite import Sprite
25
25
  from .scrollingSprite import ScrollingSprite
26
26
  from .particle import *
27
- from .animatedSprite import AnimatedSprite, AnimState
27
+ from .animatedSprite import AnimatedSprite, Animation
28
+ from .character import Character
28
29
  from .stateMachine import State, StateMachine
29
30
  from .scene import Scene
30
31
  from .gui import *
31
32
  from .sceneManager import SceneManager
32
33
  from .manager import Manager
34
+ from .templates import *
35
+ import importlib.metadata
33
36
 
34
37
 
35
38
  def init_screen(resolution: tuple[int, int], flags: int = 0, vsync: int = 0):
@@ -44,6 +47,16 @@ def init_screen(resolution: tuple[int, int], flags: int = 0, vsync: int = 0):
44
47
  )
45
48
 
46
49
 
50
+
51
+ def print_version():
52
+ package_name = "batFramework"
53
+ try:
54
+ version = importlib.metadata.version(package_name)
55
+ print(f"{package_name} version: {version}")
56
+ except importlib.metadata.PackageNotFoundError:
57
+ print(f"{package_name} is not installed")
58
+
59
+
47
60
  def init(
48
61
  resolution: tuple[int, int],
49
62
  flags: int = 0,
@@ -54,6 +67,7 @@ def init(
54
67
  window_title: str = "BatFramework Project",
55
68
  fps_limit: int = 0,
56
69
  ):
70
+ print_version()
57
71
  pygame.display.set_caption(window_title)
58
72
  init_screen(resolution, flags, vsync)
59
73
 
@@ -1,8 +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
- def search_index(target, lst):
6
+ def search_index(target: int, lst: List[int]) -> int:
6
7
  cumulative_sum = 0
7
8
  for index, value in enumerate(lst):
8
9
  cumulative_sum += value
@@ -11,65 +12,68 @@ def search_index(target, lst):
11
12
  return -1
12
13
 
13
14
 
14
- class AnimState:
15
+ class Animation:
15
16
  def __init__(
16
17
  self,
17
- name: str,
18
- surface: pygame.Surface,
19
- width,
20
- height,
21
- duration_list: list | int,
18
+ name: str
22
19
  ) -> None:
23
- self.frames: list[pygame.Surface] = list(
24
- bf.utils.split_surface(
25
- surface, width, height, False, convert_alpha
26
- ).values()
27
- )
28
- self.frames_flipX: list[pygame.Surface] = list(
29
- bf.utils.split_surface(surface, width, height, True, convert_alpha).values()
30
- )
31
-
32
20
  self.name = name
33
- self.duration_list: list[int] = []
34
- self.duration_list_length = 0
35
- self.set_duration_list(duration_list)
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
36
30
 
37
31
  def __repr__(self):
38
- return f"AnimState({self.name})"
32
+ return f"Animation({self.name})"
39
33
 
40
- def counter_to_frame(self, counter: float | int) -> int:
34
+ def counter_to_frame(self, counter: Union[float, int]) -> int:
35
+ if not self.frames :
36
+ raise ValueError("Animation has no frames")
41
37
  return search_index(
42
38
  int(counter % self.duration_list_length), self.duration_list
43
39
  )
44
40
 
45
- def get_frame(self, counter, flip):
41
+ def get_frame(self, counter: Union[float, int], flip: bool) -> pygame.Surface:
46
42
  i = self.counter_to_frame(counter)
47
43
  return self.frames_flipX[i] if flip else self.frames[i]
48
44
 
49
- def set_duration_list(self, duration_list: list[int] | int):
45
+ def set_duration_list(self, duration_list: Union[List[int], int]) -> Self:
50
46
  if isinstance(duration_list, int):
51
47
  duration_list = [duration_list] * len(self.frames)
52
48
  if len(duration_list) != len(self.frames):
53
49
  raise ValueError("duration_list should have values for all frames")
54
50
  self.duration_list = duration_list
55
51
  self.duration_list_length = sum(self.duration_list)
52
+ return self
56
53
 
57
-
58
- class AnimatedSprite(bf.DynamicEntity):
59
- def __init__(self, size=None) -> None:
54
+ class AnimatedSprite(bf.Entity):
55
+ def __init__(self, size: Optional[Tuple[int, int]] = None) -> None:
60
56
  super().__init__(size, no_surface=True)
61
57
  self.float_counter: float = 0
62
- self.animStates: dict[str, AnimState] = {}
63
- self.current_state: AnimState | None = None
64
- self.flipX = False
65
- self._locked = False
66
- self.paused: bool = False
67
-
68
- def pause(self) -> None:
69
- self.paused = True
70
-
71
- def resume(self) -> None:
72
- self.paused = False
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
73
77
 
74
78
  def toggle_pause(self) -> None:
75
79
  self.paused = not self.paused
@@ -80,16 +84,7 @@ class AnimatedSprite(bf.DynamicEntity):
80
84
  def set_frame(self, frame_index: int) -> None:
81
85
  if not self.current_state:
82
86
  return
83
- total = sum(self.current_state.duration_list)
84
- frame_index = max(0, min(total, frame_index))
85
- new_counter = 0
86
- i = 0
87
- while frame_index < total:
88
- if self.current_state.counter_to_frame(new_counter) >= frame_index:
89
- break
90
- new_counter += self.current_state.duration_list[i]
91
- i += 1
92
- self.set_counter(new_counter)
87
+ self.set_counter(sum(self.current_state.duration_list[:frame_index]))
93
88
 
94
89
  def lock(self) -> None:
95
90
  self._locked = True
@@ -97,71 +92,85 @@ class AnimatedSprite(bf.DynamicEntity):
97
92
  def unlock(self) -> None:
98
93
  self._locked = False
99
94
 
100
- def set_flipX(self, value) -> None:
95
+ def set_flipX(self, value: bool) -> None:
101
96
  self.flipX = value
102
97
 
103
- def remove_animState(self, name: str) -> bool:
104
- if not name in self.animStates:
98
+ def remove_animation(self, name: str) -> bool:
99
+ if name not in self.animations:
105
100
  return False
106
- self.animStates.pop(name)
101
+ self.animations.pop(name)
107
102
  if self.current_state and self.current_state.name == name:
108
- self.current_animState = (
109
- list(self.animStates.keys())[0] if self.animStates else ""
103
+ self.current_state = (
104
+ list(self.animations.values())[0] if self.animations else None
110
105
  )
111
106
  return True
112
107
 
113
- def add_animState(
108
+ def add_animation(
114
109
  self,
115
- name: str,
116
- surface: pygame.Surface,
117
- size: tuple[int, int],
118
- duration_list: list[int],
119
- convert_alpha: bool = True,
110
+ animation:Animation
120
111
  ) -> bool:
121
- if name in self.animStates:
112
+ if animation.name in self.animations:
122
113
  return False
123
- self.animStates[name] = AnimState(name, surface, *size, duration_list)
124
- if len(self.animStates) == 1:
125
- self.set_animState(name)
114
+ self.animations[animation.name] = animation
126
115
  return True
127
116
 
128
- def set_animState(self, state: str, reset_counter=True, lock=False) -> bool:
129
- if state not in self.animStates or self._locked:
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:
130
121
  return False
122
+
123
+ animation = self.animations[state]
124
+ self.current_state = animation
131
125
 
132
- animState = self.animStates[state]
133
- self.current_state = animState
134
-
135
- self.rect = self.current_state.frames[0].get_frect(center=self.rect.center)
126
+ if self.current_state.frames:
127
+ self.rect = self.current_state.frames[0].get_frect(center=self.rect.center)
136
128
 
137
- if reset_counter or self.float_counter > sum(animState.duration_list):
129
+ if reset_counter or (self.float_counter > animation.duration_list_length):
138
130
  self.float_counter = 0
131
+
139
132
  if lock:
140
133
  self.lock()
134
+
135
+ if self.transition_end_animation is not None:
136
+ self.transition_end_animation = None
141
137
  return True
142
-
143
- def get_animState(self) -> AnimState | None:
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]:
144
145
  return self.current_state
145
146
 
146
147
  def update(self, dt: float) -> None:
147
- s = self.get_animState()
148
- if not self.animStates or s is None:
149
- return
150
- if not self.paused:
151
- self.float_counter += 60 * dt
152
- if self.float_counter > s.duration_list_length:
153
- self.float_counter = 0
154
- self.do_update(dt)
155
-
156
- def draw(self, camera: bf.Camera) -> int:
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:
157
166
  if (
158
167
  not self.visible
159
168
  or not camera.rect.colliderect(self.rect)
160
169
  or not self.current_state
161
170
  ):
162
- return 0
171
+ return
163
172
  camera.surface.blit(
164
- self.current_state.get_frame(self.float_counter, self.flipX),
173
+ self.get_current_frame(),
165
174
  camera.world_to_screen(self.rect),
166
175
  )
167
- return 1
176
+ return
@@ -68,7 +68,7 @@ class AudioManager(metaclass=bf.Singleton):
68
68
  self.sounds[name]["sound"].play()
69
69
  return True
70
70
  except KeyError:
71
- # print(f"Sound '{name}' not loaded in AudioManager.")
71
+ print(f"Sound '{name}' not loaded in AudioManager.")
72
72
  return False
73
73
 
74
74
  def stop_sound(self, name) -> bool:
@@ -77,7 +77,7 @@ class AudioManager(metaclass=bf.Singleton):
77
77
  return True
78
78
  except KeyError:
79
79
  return False
80
- # print(f"Sound '{name}' not loaded in AudioManager.")
80
+ print(f"Sound '{name}' not loaded in AudioManager.")
81
81
 
82
82
  def load_music(self, name, path):
83
83
  self.musics[name] = bf.ResourceManager().get_path(path)
@@ -0,0 +1,27 @@
1
+ import batFramework as bf
2
+ from .stateMachine import State
3
+ from .animatedSprite import AnimatedSprite
4
+ from .dynamicEntity import DynamicEntity
5
+
6
+ class Character(AnimatedSprite,DynamicEntity):
7
+ def __init__(self) -> None:
8
+ super().__init__()
9
+ self.state_machine = bf.StateMachine(self)
10
+ self.do_setup_animations()
11
+ self.do_setup_states()
12
+
13
+ def set_state(self,state_name:str):
14
+ self.state_machine.set_state(state_name)
15
+
16
+ def get_current_state(self)->State:
17
+ return self.state_machine.get_current_state()
18
+
19
+ def update(self, dt: float) -> None:
20
+ self.state_machine.update(dt)
21
+ super().update(dt)
22
+
23
+ def do_setup_states(self):
24
+ pass
25
+
26
+ def do_setup_animations(self):
27
+ pass
batFramework/cutscene.py CHANGED
@@ -1,8 +1,11 @@
1
1
  import batFramework as bf
2
-
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  class CutsceneBlock: ...
5
5
 
6
+ if TYPE_CHECKING:
7
+ from .cutsceneBlocks import CutsceneBlock
8
+
6
9
 
7
10
  class Cutscene: ...
8
11
 
@@ -58,7 +61,7 @@ class CutsceneManager(metaclass=bf.Singleton):
58
61
 
59
62
  class Cutscene:
60
63
  def __init__(self) -> None:
61
- self.cutscene_blocks = []
64
+ self.cutscene_blocks : list[CutsceneBlock] = []
62
65
  self.block_index = 0
63
66
  self.end_blocks: list[CutsceneBlock] = []
64
67
  self.ended = False
@@ -1,7 +1,7 @@
1
1
  import batFramework as bf
2
2
  from .cutscene import Cutscene, CutsceneManager
3
3
  from .transition import *
4
-
4
+ from typing import Optional,Callable
5
5
 
6
6
  # Define the base CutsceneBlock class
7
7
  class CutsceneBlock:
@@ -150,7 +150,6 @@ class SceneTransitionBlock(CutsceneBlock):
150
150
  # Start the timer to handle the end of the transition
151
151
  self.timer.start()
152
152
 
153
-
154
153
  class DelayBlock(CutsceneBlock):
155
154
  def __init__(self, duration) -> None:
156
155
  super().__init__()
@@ -160,12 +159,11 @@ class DelayBlock(CutsceneBlock):
160
159
  super().start()
161
160
  self.timer.start()
162
161
 
163
-
164
162
  class FunctionBlock(CutsceneBlock):
165
- def __init__(self, func) -> None:
163
+ def __init__(self, func : Optional[Callable]) -> None:
166
164
  self.function = func
167
165
 
168
166
  def start(self):
169
167
  super().start()
170
- self.function()
168
+ if self.function : self.function()
171
169
  self.end()
@@ -8,15 +8,22 @@ class DynamicEntity(bf.Entity):
8
8
  self,
9
9
  size: None | tuple[int, int] = None,
10
10
  surface_flags: int = 0,
11
- convert_alpha: bool = False,
11
+ convert_alpha: bool = False,*args,**kwargs
12
12
  ) -> None:
13
- super().__init__(size, surface_flags, convert_alpha)
13
+ super().__init__(size, surface_flags, convert_alpha,*args,**kwargs)
14
14
  self.velocity = pygame.math.Vector2(0, 0)
15
+ self.ignore_collisions : bool = False
15
16
 
16
- def on_collideX(self, collider: Self):
17
+ def on_collideX(self, collider: "DynamicEntity"):
18
+ """
19
+ Return true if collision
20
+ """
17
21
  return False
18
22
 
19
- def on_collideY(self, collider: Self):
23
+ def on_collideY(self, collider: "DynamicEntity"):
24
+ """
25
+ Return true if collision
26
+ """
20
27
  return False
21
28
 
22
29
  def move_by_velocity(self, dt) -> None:
batFramework/enums.py CHANGED
@@ -51,7 +51,7 @@ class easing(Enum):
51
51
  EASE_IN = (0.95, 0, 1, 0.55)
52
52
  EASE_OUT = (0.5, 1, 0.5, 1)
53
53
  EASE_IN_OUT = (0.55, 0, 0.45, 1)
54
- EASE_IN_OUT_ELASTIC = (0.39, -0.55, 0.3, 1.3)
54
+ EASE_IN_OUT_ELASTIC = (0.76,-0.36,0.41,1.34)
55
55
 
56
56
  def __init__(self, *control_points):
57
57
  self.control_points = control_points
@@ -110,4 +110,4 @@ class actionType(Enum):
110
110
  class textMode(Enum):
111
111
  ALPHABETICAL = 0
112
112
  NUMERICAL = 1
113
- ALPHANUMERIC = 3
113
+ ALPHANUMERICAL = 3
@@ -42,7 +42,7 @@ class FontManager(metaclass=Singleton):
42
42
  filename = name # if name is not given, name is the filename
43
43
  self.FONTS[filename] = {}
44
44
  # fill the dict
45
- for size in range(self.MIN_FONT_SIZE, self.MAX_FONT_SIZE, 2):
45
+ for size in range(self.MIN_FONT_SIZE, self.MAX_FONT_SIZE+1, 2):
46
46
  self.FONTS[filename][size] = pygame.font.Font(path, size=size)
47
47
 
48
48
  def load_sysfont(self, font_name: str | None, key: str | None = ""):
@@ -52,7 +52,7 @@ class FontManager(metaclass=Singleton):
52
52
  raise FileNotFoundError(f"Requested font '{font_name}' was not found")
53
53
  self.FONTS[font_name] = {}
54
54
 
55
- for size in range(self.MIN_FONT_SIZE, self.MAX_FONT_SIZE, 2):
55
+ for size in range(self.MIN_FONT_SIZE, self.MAX_FONT_SIZE+1, 2):
56
56
  self.FONTS[key][size] = pygame.font.SysFont(font_name, size=size)
57
57
 
58
58
  def get_font(
@@ -27,7 +27,7 @@ class ClickableWidget(Shape, InteractiveWidget):
27
27
  self.set_debug_color("cyan")
28
28
  self.set_relief(self.unpressed_relief)
29
29
 
30
- def set_unpressed_relief(self, relief: int = 0) -> Self:
30
+ def set_unpressed_relief(self, relief: int) -> Self:
31
31
  if relief == self.unpressed_relief:
32
32
  return self
33
33
  self.unpressed_relief = relief
@@ -36,18 +36,17 @@ class ClickableWidget(Shape, InteractiveWidget):
36
36
  self.set_relief(relief)
37
37
  return self
38
38
 
39
- def set_silent_focus(self, value: bool) -> Self:
40
- self.silent_focus = value
41
- return self
42
-
43
- def set_pressed_relief(self, relief: int = 0) -> Self:
39
+ def set_pressed_relief(self, relief: int) -> Self:
44
40
  if relief == self.pressed_relief:
45
41
  return self
46
42
  self.pressed_relief = relief
47
43
  self.dirty_shape = True
48
44
  if self.is_pressed:
49
45
  self.set_relief(relief)
46
+ return self
50
47
 
48
+ def set_silent_focus(self, value: bool) -> Self:
49
+ self.silent_focus = value
51
50
  return self
52
51
 
53
52
  def set_click_down_sound(self, sound_name: str) -> Self:
@@ -141,15 +140,16 @@ class ClickableWidget(Shape, InteractiveWidget):
141
140
  if not self.get_focus():
142
141
  return
143
142
  self.is_pressed = True
144
- bf.AudioManager().play_sound(self.click_down_sound)
145
-
143
+ if self.click_down_sound:
144
+ bf.AudioManager().play_sound(self.click_down_sound)
146
145
  pygame.mouse.set_cursor(self.click_cursor)
147
146
  self.set_relief(self.pressed_relief)
148
147
 
149
148
  def do_on_click_up(self, button) -> None:
150
149
  if self.enabled and button == 1 and self.is_pressed:
151
150
  self.is_pressed = False
152
- bf.AudioManager().play_sound(self.click_up_sound)
151
+ if self.click_up_sound:
152
+ bf.AudioManager().play_sound(self.click_up_sound)
153
153
  self.set_relief(self.unpressed_relief)
154
154
  self.click()
155
155
 
@@ -217,3 +217,4 @@ class ClickableWidget(Shape, InteractiveWidget):
217
217
  self._paint_disabled()
218
218
  elif self.is_hovered:
219
219
  self._paint_hovered()
220
+