batframework 1.0.9a7__py3-none-any.whl → 1.0.9a8__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 (61) hide show
  1. batFramework/__init__.py +20 -11
  2. batFramework/action.py +1 -1
  3. batFramework/animatedSprite.py +47 -116
  4. batFramework/animation.py +30 -5
  5. batFramework/audioManager.py +8 -5
  6. batFramework/baseScene.py +240 -0
  7. batFramework/camera.py +4 -0
  8. batFramework/constants.py +6 -1
  9. batFramework/cutscene.py +221 -21
  10. batFramework/cutsceneManager.py +5 -2
  11. batFramework/drawable.py +7 -5
  12. batFramework/easingController.py +10 -11
  13. batFramework/entity.py +21 -2
  14. batFramework/enums.py +48 -33
  15. batFramework/gui/__init__.py +3 -1
  16. batFramework/gui/animatedLabel.py +10 -2
  17. batFramework/gui/button.py +4 -31
  18. batFramework/gui/clickableWidget.py +42 -30
  19. batFramework/gui/constraints/constraints.py +212 -136
  20. batFramework/gui/container.py +72 -48
  21. batFramework/gui/debugger.py +12 -17
  22. batFramework/gui/draggableWidget.py +8 -11
  23. batFramework/gui/image.py +3 -10
  24. batFramework/gui/indicator.py +73 -1
  25. batFramework/gui/interactiveWidget.py +117 -100
  26. batFramework/gui/label.py +73 -63
  27. batFramework/gui/layout.py +221 -452
  28. batFramework/gui/meter.py +21 -7
  29. batFramework/gui/radioButton.py +0 -1
  30. batFramework/gui/root.py +99 -29
  31. batFramework/gui/selector.py +257 -0
  32. batFramework/gui/shape.py +13 -5
  33. batFramework/gui/slider.py +260 -93
  34. batFramework/gui/textInput.py +45 -21
  35. batFramework/gui/toggle.py +70 -52
  36. batFramework/gui/tooltip.py +30 -0
  37. batFramework/gui/widget.py +203 -125
  38. batFramework/manager.py +7 -8
  39. batFramework/particle.py +4 -1
  40. batFramework/propertyEaser.py +79 -0
  41. batFramework/renderGroup.py +17 -50
  42. batFramework/resourceManager.py +43 -13
  43. batFramework/scene.py +15 -335
  44. batFramework/sceneLayer.py +138 -0
  45. batFramework/sceneManager.py +31 -36
  46. batFramework/scrollingSprite.py +8 -3
  47. batFramework/sprite.py +1 -1
  48. batFramework/templates/__init__.py +1 -2
  49. batFramework/templates/controller.py +97 -0
  50. batFramework/timeManager.py +76 -22
  51. batFramework/transition.py +37 -103
  52. batFramework/utils.py +121 -3
  53. {batframework-1.0.9a7.dist-info → batframework-1.0.9a8.dist-info}/METADATA +24 -3
  54. batframework-1.0.9a8.dist-info/RECORD +66 -0
  55. {batframework-1.0.9a7.dist-info → batframework-1.0.9a8.dist-info}/WHEEL +1 -1
  56. batFramework/character.py +0 -27
  57. batFramework/templates/character.py +0 -43
  58. batFramework/templates/states.py +0 -166
  59. batframework-1.0.9a7.dist-info/RECORD +0 -63
  60. /batframework-1.0.9a7.dist-info/LICENCE → /batframework-1.0.9a8.dist-info/LICENSE +0 -0
  61. {batframework-1.0.9a7.dist-info → batframework-1.0.9a8.dist-info}/top_level.txt +0 -0
batFramework/manager.py CHANGED
@@ -3,7 +3,7 @@ import pygame
3
3
  import asyncio
4
4
 
5
5
  class Manager(bf.SceneManager):
6
- def __init__(self, *initial_scene_list) -> None:
6
+ def __init__(self, *initial_scenes) -> None:
7
7
  super().__init__()
8
8
  self.debug_mode: bf.enums.debugMode = bf.debugMode.HIDDEN
9
9
  self.screen: pygame.Surface | None = bf.const.SCREEN
@@ -18,8 +18,8 @@ class Manager(bf.SceneManager):
18
18
  bf.ResourceManager().set_sharedVar("debug_mode", self.debug_mode)
19
19
 
20
20
  self.do_pre_init()
21
- if initial_scene_list:
22
- self.init_scenes(*initial_scene_list)
21
+ if initial_scenes:
22
+ self.init_scenes(*initial_scenes)
23
23
  self.do_init()
24
24
 
25
25
  @staticmethod
@@ -75,15 +75,15 @@ class Manager(bf.SceneManager):
75
75
  self.print_status()
76
76
  return
77
77
  self.cutsceneManager.process_event(event)
78
+ if event.type == pygame.VIDEORESIZE and not (bf.const.FLAGS & pygame.SCALED):
79
+ bf.const.set_resolution((event.w, event.h))
80
+
78
81
  if event.consumed: return
82
+
79
83
  super().process_event(event)
80
84
  if not event.consumed:
81
85
  if event.type == pygame.QUIT:
82
86
  self.running = False
83
- elif event.type == pygame.VIDEORESIZE and not (
84
- bf.const.FLAGS & pygame.SCALED
85
- ):
86
- bf.const.set_resolution((event.w, event.h))
87
87
 
88
88
  def update(self, dt: float) -> None:
89
89
  self.timeManager.update(dt)
@@ -96,7 +96,6 @@ class Manager(bf.SceneManager):
96
96
  raise Exception("Manager can't start without scenes")
97
97
  if self.running:
98
98
  raise Exception("Error : Already running")
99
- return
100
99
  self.is_async_running = True
101
100
  self.running = True
102
101
  dt: float = 0
batFramework/particle.py CHANGED
@@ -82,6 +82,7 @@ class ParticleGenerator(bf.Drawable):
82
82
  def __init__(self) -> None:
83
83
  super().__init__((0, 0))
84
84
  self.particles: list[Particle] = []
85
+ self.count = 0
85
86
 
86
87
  def get_debug_outlines(self):
87
88
  return
@@ -96,10 +97,11 @@ class ParticleGenerator(bf.Drawable):
96
97
  particle.generator = self
97
98
  particle.do_when_added()
98
99
  self.particles.append(particle)
100
+ self.count += 1
99
101
 
100
102
  def clear(self):
101
103
  self.particles = []
102
-
104
+ self.count = 0
103
105
  def update(self, dt: float):
104
106
  particles_to_remove = []
105
107
  for particle in self.particles:
@@ -108,6 +110,7 @@ class ParticleGenerator(bf.Drawable):
108
110
  particles_to_remove.append(particle)
109
111
  for p in particles_to_remove:
110
112
  self.particles.remove(p)
113
+ self.count -= 1
111
114
 
112
115
  def draw(self, camera) -> None:
113
116
  camera.surface.fblits(
@@ -0,0 +1,79 @@
1
+ from .easingController import EasingController
2
+ import pygame
3
+ from typing import Callable, Any, Self
4
+ import batFramework as bf
5
+
6
+ class PropertyEaser(EasingController):
7
+ class EasedProperty:
8
+ def __init__(self, getter: Callable[[], Any], setter: Callable[[Any], None], end_value: Any, start_value: Any = None):
9
+ self.getter = getter
10
+ self.setter = setter
11
+ self.start_value = start_value if start_value is not None else getter()
12
+ self.end_value = end_value
13
+
14
+ def interpolate(self, t: float):
15
+ a, b = self.start_value, self.end_value
16
+ if isinstance(a, (int, float)):
17
+ return a + (b - a) * t
18
+ elif isinstance(a, pygame.Vector2):
19
+ return a.lerp(b, t)
20
+ elif isinstance(a, pygame.Color):
21
+ return pygame.Color(
22
+ round(a.r + (b.r - a.r) * t),
23
+ round(a.g + (b.g - a.g) * t),
24
+ round(a.b + (b.b - a.b) * t),
25
+ round(a.a + (b.a - a.a) * t),
26
+ )
27
+ else:
28
+ raise TypeError(f"Unsupported type for interpolation: {type(a)}")
29
+
30
+ def apply(self, t: float):
31
+ self.setter(self.interpolate(t))
32
+
33
+ def __init__(
34
+ self,
35
+ duration: float = 1,
36
+ easing: bf.easing = bf.easing.LINEAR,
37
+ loop: int = 0,
38
+ register: str = "global",
39
+ end_callback: Callable[[], Any] = None,
40
+ ):
41
+ self.properties: list[PropertyEaser.EasedProperty] = []
42
+
43
+ def update_all(progress):
44
+ for prop in self.properties:
45
+ prop.apply(progress)
46
+
47
+ super().__init__(duration, easing, update_all, end_callback, loop, register)
48
+
49
+ def __str__(self):
50
+ return f"(PROP){super().__str__()}"
51
+
52
+ def add_attr(
53
+ self,
54
+ obj: Any,
55
+ attr: str,
56
+ end_value: Any,
57
+ start_value: Any = None,
58
+ )->Self:
59
+ self.properties.append(
60
+ PropertyEaser.EasedProperty(
61
+ getter=lambda o=obj, a=attr: getattr(o, a),
62
+ setter=lambda v, o=obj, a=attr: setattr(o, a, v),
63
+ end_value=end_value,
64
+ start_value=start_value,
65
+ )
66
+ )
67
+ return self
68
+
69
+ def add_custom(
70
+ self,
71
+ getter: Callable[[], Any],
72
+ setter: Callable[[Any], None],
73
+ end_value: Any,
74
+ start_value: Any = None,
75
+ )->Self:
76
+ self.properties.append(
77
+ PropertyEaser.EasedProperty(getter, setter, end_value, start_value)
78
+ )
79
+ return self
@@ -1,11 +1,6 @@
1
1
  import batFramework as bf
2
2
  import pygame
3
- from typing import Self, Iterator, Callable
4
-
5
- """
6
- + same render order
7
- + fblits
8
- """
3
+ from typing import Callable, Iterator
9
4
 
10
5
 
11
6
  class RenderGroup(bf.Drawable):
@@ -14,54 +9,26 @@ class RenderGroup(bf.Drawable):
14
9
  ) -> None:
15
10
  super().__init__()
16
11
  self.entity_iterator = entity_iterator
17
- # self.set_blit_flags(blit_flags)
12
+ self.blit_flags = blit_flags
18
13
  self.set_debug_color("white")
19
14
 
20
- def get_debug_outlines(self):
21
- # yield (self.rect, self.debug_color)
22
- for e in self.entity_iterator():
23
- yield from e.get_debug_outlines()
24
-
25
- def set_parent_scene(self, scene) -> Self:
26
- self.parent_scene = scene
27
- for e in self.entity_iterator():
28
- e.set_parent_scene(scene)
29
- return self
30
-
31
- def process_event(self, event: pygame.Event) -> bool:
32
- """
33
- Returns bool : True if the method is blocking (no propagation to next children of the scene)
34
- """
35
- self.do_process_actions(event)
36
- res = self.do_handle_event(event)
37
- if res:
38
- return res
39
- for e in self.entity_iterator():
40
- if e.process_event(event):
41
- return True
42
- return False
15
+ def draw(self, camera: bf.Camera) -> None:
16
+ if not self.visible:
17
+ return
43
18
 
44
- def update(self, dt: float) -> None:
45
- """
46
- Update method to be overriden by subclasses of entity
47
- """
19
+ fblits_data = []
48
20
  for e in self.entity_iterator():
49
- e.update(dt)
50
- # gen = self.entity_iterator()
51
- # self.rect = next(gen).rect.unionall([e.rect for e in gen])
21
+ if not getattr(e, "drawn_by_group", False):
22
+ # Set flag to skip their individual draw call
23
+ e.drawn_by_group = True
52
24
 
53
- self.do_update(dt)
54
- self.do_reset_actions()
25
+ if e.visible and camera.rect.colliderect(e.rect):
26
+ fblits_data.append(
27
+ (e.surface, (e.rect.x - camera.rect.x, e.rect.y - camera.rect.y))
28
+ )
55
29
 
56
- def draw(self, camera: bf.Camera) -> None:
57
- """
58
- Draw the entity onto the camera with coordinate transposing
59
- """
60
- if not self.visible:
61
- return
62
- fblits_data = (
63
- (e.surface, (e.rect.x - camera.rect.x, e.rect.y - camera.rect.y))
64
- for e in self.entity_iterator()
65
- if camera.rect.colliderect(e.rect)
66
- )
67
30
  camera.surface.fblits(fblits_data, self.blit_flags)
31
+
32
+ def get_debug_outlines(self):
33
+ for e in self.entity_iterator():
34
+ yield from e.get_debug_outlines()
@@ -3,8 +3,10 @@ import os
3
3
  import pygame
4
4
  import sys
5
5
  import json
6
- from typing import Any
6
+ from typing import Any, Callable
7
7
  from .utils import Singleton
8
+ import asyncio
9
+
8
10
 
9
11
  if getattr(sys, "frozen", False):
10
12
  # If the application is run as a bundle, the PyInstaller bootloader
@@ -17,32 +19,50 @@ else:
17
19
 
18
20
  class ResourceManager(metaclass=Singleton):
19
21
  def __init__(self):
20
- self.shared_variables: dict[str,Any] = {}
22
+ self.shared_variables: dict[str, Any] = {}
21
23
  self.convert_image_cache = {}
22
24
  self.convert_alpha_image_cache = {}
23
25
  self.sound_cache = {}
24
26
  self.RESOURCE_PATH = "."
27
+ self.loading_thread = None
25
28
 
26
- def load_dir(self, path) -> None:
27
- for root, dirs, files in os.walk(path):
28
- files = [f for f in files if not f[0] == "."]
29
- dirs[:] = [d for d in dirs if not d[0] == "."]
29
+ def load_resources(self, path: str, progress_callback: Callable[[float], Any] = None):
30
+ """
31
+ loads resources from a directory.
32
+ Progress is reported through the callback.
33
+ Supposed to be asynchronous but don't use it as such yet
34
+ """
35
+ self.progress_callback = progress_callback
36
+
37
+ total_files = sum(
38
+ len(files) for _, _, files in os.walk(path) if not any(f.startswith(".") for f in files)
39
+ )
40
+
41
+ loaded_files = 0
30
42
 
43
+ for root, dirs, files in os.walk(path):
44
+ files = [f for f in files if not f.startswith(".")]
45
+ dirs[:] = [d for d in dirs if not (d.startswith(".") or d.startswith("__"))]
31
46
  for file in files:
32
47
  file_path = os.path.join(root, file)
48
+
49
+ # Simulate resource loading
50
+ # await asyncio.sleep(0) # Yield control to the event loop
51
+
33
52
  if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif")):
34
53
  self.load_image(file_path)
35
- # print(f"Loaded image : '{file_path}'")
36
-
37
- elif file.lower().endswith((".mp3", ".wav")):
54
+ elif file.lower().endswith((".mp3", ".wav", ".ogg")):
38
55
  bf.AudioManager().load_sound(file.split(".")[0], file_path)
39
- # print(f"Loaded sound : '{file_path}'")
40
-
41
56
  elif file.lower().endswith((".ttf", ".otf")):
42
57
  bf.FontManager().load_font(file_path, file.split(".")[0])
43
- # print(f"Loaded font : '{file_path}'")
44
58
 
45
- print(f"Loaded resources in directory : '{path}'")
59
+ loaded_files += 1
60
+ # Report progress
61
+ # if self.progress_callback:
62
+ # self.progress_callback(loaded_files / total_files)
63
+
64
+ print(f"Loaded resources in directory: '{path}'")
65
+
46
66
 
47
67
  def set_resource_path(self, path: str):
48
68
  self.RESOURCE_PATH = os.path.join(application_path, path)
@@ -60,6 +80,7 @@ class ResourceManager(metaclass=Singleton):
60
80
  self.convert_image_cache[key] = pygame.image.load(path).convert()
61
81
  self.convert_alpha_image_cache[key] = pygame.image.load(path).convert_alpha()
62
82
 
83
+
63
84
  def get_image(self, path, convert_alpha: bool = False) -> pygame.Surface | None:
64
85
  key = self.get_path(path)
65
86
  return (
@@ -93,6 +114,15 @@ class ResourceManager(metaclass=Singleton):
93
114
  self.shared_variables[name] = value
94
115
  return True
95
116
 
117
+ def set_sharedVars(self, variables: dict) -> bool:
118
+ """
119
+ Set multiple shared variables at once. This will be accessible (read/write) from any scene.
120
+ """
121
+ if not isinstance(variables, dict):
122
+ raise ValueError("Input must be a dictionary")
123
+ self.shared_variables.update(variables)
124
+ return True
125
+
96
126
  def get_sharedVar(self, name, error_value=None):
97
127
  """
98
128
  Get a shared variable