batframework 1.0.7__py3-none-any.whl → 1.0.8a1__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 (59) hide show
  1. batFramework/__init__.py +24 -15
  2. batFramework/action.py +95 -106
  3. batFramework/actionContainer.py +11 -8
  4. batFramework/animatedSprite.py +60 -43
  5. batFramework/audioManager.py +52 -22
  6. batFramework/camera.py +87 -72
  7. batFramework/constants.py +19 -41
  8. batFramework/cutscene.py +12 -11
  9. batFramework/cutsceneBlocks.py +12 -14
  10. batFramework/dynamicEntity.py +5 -4
  11. batFramework/easingController.py +58 -0
  12. batFramework/entity.py +37 -130
  13. batFramework/enums.py +93 -3
  14. batFramework/fontManager.py +15 -7
  15. batFramework/gui/__init__.py +5 -1
  16. batFramework/gui/button.py +6 -144
  17. batFramework/gui/clickableWidget.py +206 -0
  18. batFramework/gui/constraints/__init__.py +1 -0
  19. batFramework/gui/constraints/constraints.py +378 -0
  20. batFramework/gui/container.py +147 -34
  21. batFramework/gui/debugger.py +39 -22
  22. batFramework/gui/dialogueBox.py +69 -43
  23. batFramework/gui/draggableWidget.py +38 -0
  24. batFramework/gui/image.py +33 -28
  25. batFramework/gui/indicator.py +30 -16
  26. batFramework/gui/interactiveWidget.py +72 -20
  27. batFramework/gui/label.py +240 -98
  28. batFramework/gui/layout.py +125 -53
  29. batFramework/gui/meter.py +76 -0
  30. batFramework/gui/radioButton.py +62 -0
  31. batFramework/gui/root.py +72 -48
  32. batFramework/gui/shape.py +257 -49
  33. batFramework/gui/slider.py +217 -2
  34. batFramework/gui/textInput.py +106 -60
  35. batFramework/gui/toggle.py +85 -42
  36. batFramework/gui/widget.py +259 -288
  37. batFramework/manager.py +30 -16
  38. batFramework/object.py +115 -0
  39. batFramework/{particles.py → particle.py} +37 -34
  40. batFramework/renderGroup.py +62 -0
  41. batFramework/resourceManager.py +36 -24
  42. batFramework/scene.py +94 -88
  43. batFramework/sceneManager.py +140 -57
  44. batFramework/scrollingSprite.py +113 -0
  45. batFramework/sprite.py +35 -23
  46. batFramework/tileset.py +34 -52
  47. batFramework/time.py +105 -56
  48. batFramework/transition.py +213 -1
  49. batFramework/utils.py +38 -18
  50. {batframework-1.0.7.dist-info → batframework-1.0.8a1.dist-info}/METADATA +1 -1
  51. batframework-1.0.8a1.dist-info/RECORD +56 -0
  52. {batframework-1.0.7.dist-info → batframework-1.0.8a1.dist-info}/WHEEL +1 -1
  53. batFramework/easing.py +0 -76
  54. batFramework/gui/constraints.py +0 -277
  55. batFramework/gui/frame.py +0 -25
  56. batFramework/transitionManager.py +0 -0
  57. batframework-1.0.7.dist-info/RECORD +0 -50
  58. {batframework-1.0.7.dist-info → batframework-1.0.8a1.dist-info}/LICENCE +0 -0
  59. {batframework-1.0.7.dist-info → batframework-1.0.8a1.dist-info}/top_level.txt +0 -0
batFramework/manager.py CHANGED
@@ -6,52 +6,66 @@ import random
6
6
  class Manager(bf.SceneManager):
7
7
  def __init__(self, *initial_scene_list) -> None:
8
8
  # random.seed("random")
9
- self._screen: pygame.Surface|None = bf.const.SCREEN
9
+ super().__init__()
10
+ self._screen: pygame.Surface | None = bf.const.SCREEN
10
11
  self._timeManager = bf.TimeManager()
11
12
  self._cutsceneManager = bf.CutsceneManager()
12
13
  self._cutsceneManager.set_manager(self)
13
14
  self._clock: pygame.Clock = pygame.Clock()
15
+ pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
14
16
  self.do_pre_init()
15
- super().__init__(*initial_scene_list)
17
+ self.init_scenes(*initial_scene_list)
16
18
  self.set_sharedVar("clock", self._clock)
17
19
  self.do_init()
18
20
 
19
21
  @staticmethod
20
- def set_icon(path: str)->None:
22
+ def set_icon(path: str) -> None:
21
23
  surf = pygame.image.load(bf.ResourceManager().get_path(path)).convert_alpha()
22
24
  pygame.display.set_icon(surf)
23
25
 
24
- def get_fps(self)->float:
26
+ def print_status(self):
27
+ super().print_status()
28
+ print("TIMERS : ")
29
+ for r in self._timeManager.get_active_registers():
30
+ # print(r["timers"])
31
+ print("\n".join(str(t) for t in r))
32
+ print("-" * 40)
33
+
34
+ def get_fps(self) -> float:
25
35
  return self._clock.get_fps()
26
36
 
27
- def do_init(self)->None:
37
+ def do_init(self) -> None:
28
38
  pass
29
39
 
30
- def do_pre_init(self)->None:
40
+ def do_pre_init(self) -> None:
31
41
  pass
42
+
32
43
  def stop(self) -> None:
33
44
  self._running = False
34
45
 
35
- def run(self)->None:
46
+ def run(self) -> None:
36
47
  self._running = True
37
48
  dt: float = 0
38
49
  while self._running:
39
50
  for event in pygame.event.get():
40
- if event.type == pygame.QUIT:
41
- self._running = False
42
- break
43
- if event.type == pygame.VIDEORESIZE:
44
- bf.const.set_resolution((event.w, event.h))
51
+ event.consumed = False
45
52
  self.process_event(event)
53
+ if not event.consumed:
54
+ if event.type == pygame.QUIT:
55
+ self._running = False
56
+ break
57
+ if event.type == pygame.VIDEORESIZE and not (
58
+ bf.const.FLAGS & pygame.SCALED
59
+ ):
60
+ bf.const.set_resolution((event.w, event.h))
46
61
  # update
47
- # dt = self._clock.tick(bf.const.FPS if not bf.const.VSYNC else 0) / 1000
48
- dt = self._clock.tick(0 if bf.const.VSYNC else bf.const.FPS) / 1000
62
+ dt = self._clock.tick(bf.const.FPS) / 1000
49
63
  # dt = min(dt, 0.02) dirty fix for dt being too high when window not focused for a long time
50
- self._cutsceneManager.update(dt)
51
64
  self._timeManager.update(dt)
65
+ self._cutsceneManager.update(dt)
52
66
  self.update(dt)
53
67
  # render
54
- self._screen.fill((0,0,0))
68
+ self._screen.fill((0, 0, 0))
55
69
  self.draw(self._screen)
56
70
  pygame.display.flip()
57
71
  pygame.quit()
batFramework/object.py ADDED
@@ -0,0 +1,115 @@
1
+ from typing import Any, Self
2
+ import pygame
3
+ import batFramework as bf
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from .camera import Camera
8
+
9
+
10
+ class Object:
11
+ __instance_count = 0
12
+
13
+ def __init__(self) -> None:
14
+ self.rect = pygame.FRect(0, 0, 0, 0)
15
+ self.tags: list[str] = []
16
+ self.parent_scene: bf.Scene | None = None
17
+ self.debug_color: tuple | str = "red"
18
+ self.render_order: int = 0
19
+ self.uid: int = Object.__instance_count
20
+ Object.__instance_count += 1
21
+
22
+ @staticmethod
23
+ def new_uid() -> int:
24
+ i = Object.__instance_count
25
+ Object.__instance_count += 1
26
+ return i
27
+
28
+ def set_position(self,x,y)->Self:
29
+ self.rect.topleft = x,y
30
+ return self
31
+
32
+ def set_center(self,x,y)->Self:
33
+ self.rect.center = x,y
34
+ return self
35
+
36
+ def get_debug_outlines(self):
37
+ yield (self.rect, self.debug_color)
38
+
39
+ def set_debug_color(self, color) -> Self:
40
+ self.debug_color = color
41
+ return self
42
+
43
+ def set_parent_scene(self, scene) -> Self:
44
+ if scene == self.parent_scene : return self
45
+ if self.parent_scene is not None:
46
+ self.do_when_removed()
47
+ self.parent_scene = scene
48
+ if scene is not None:
49
+ self.do_when_added()
50
+ return self
51
+
52
+ def do_when_added(self):
53
+ pass
54
+
55
+ def do_when_removed(self):
56
+ pass
57
+
58
+ def set_uid(self, uid: int) -> Self:
59
+ self.uid = uid
60
+ return self
61
+
62
+ def get_uid(self) -> int:
63
+ return self.uid
64
+
65
+ def add_tags(self, *tags) -> Self:
66
+ for tag in tags:
67
+ if tag not in self.tags:
68
+ self.tags.append(tag)
69
+ self.tags.sort()
70
+ return self
71
+
72
+ def remove_tags(self, *tags):
73
+ self.tags = [tag for tag in self.tags if tag not in tags]
74
+
75
+ def has_tags(self, *tags) -> bool:
76
+ return all(tag in self.tags for tag in tags)
77
+
78
+ def get_tags(self) -> list[str]:
79
+ return self.tags
80
+
81
+ def process_event(self, event: pygame.Event) -> bool:
82
+ """
83
+ Returns bool : True if the method is blocking (no propagation to next object of the scene)
84
+ """
85
+ if event.consumed : return
86
+ self.do_process_actions(event)
87
+ self.do_handle_event(event)
88
+
89
+ def do_process_actions(self, event: pygame.Event) -> None:
90
+ """
91
+ Process entity actions you may have set
92
+ """
93
+
94
+ def do_reset_actions(self) -> None:
95
+ """
96
+ Reset entity actions you may have set
97
+ """
98
+
99
+ def do_handle_event(self, event: pygame.Event):
100
+ """
101
+ Handle specific events with no action support
102
+ """
103
+ return False
104
+
105
+ def update(self, dt: float) -> None:
106
+ """
107
+ Update method to be overriden by subclasses of object (must call do_update and do_reset_actions)
108
+ """
109
+ self.do_update(dt)
110
+ self.do_reset_actions()
111
+
112
+ def do_update(self, dt: float) -> None:
113
+ """
114
+ Update method to be overriden for specific behavior by the end user
115
+ """
@@ -8,6 +8,9 @@ class Particle:
8
8
  self.dead = False
9
9
  self.surface = None
10
10
 
11
+ def update(self, dt):
12
+ pass
13
+
11
14
  def kill(self):
12
15
  self.dead = True
13
16
 
@@ -18,16 +21,7 @@ class Particle:
18
21
  class TimedParticle(Particle):
19
22
  def __init__(self, duration):
20
23
  super().__init__()
21
- self.start_time = pygame.time.get_ticks()
22
- self.duration = duration
23
- self.progression = 0
24
-
25
- def update(self, dt):
26
- if self.dead:
27
- return
28
- elapsed_time = pygame.time.get_ticks() - self.start_time
29
- self.progression = elapsed_time / self.duration
30
- self.dead = elapsed_time >= self.duration
24
+ self.timer = bf.Timer(duration, end_callback=self.kill).start()
31
25
 
32
26
 
33
27
  class BasicParticle(TimedParticle):
@@ -35,16 +29,22 @@ class BasicParticle(TimedParticle):
35
29
  self,
36
30
  start_pos: tuple[float, float],
37
31
  start_vel: tuple[float, float],
38
- duration=1000,
32
+ duration=1,
39
33
  color=None,
40
- size=(4, 4),
34
+ size: tuple[int, int] = (4, 4),
35
+ *args,
36
+ **kwargs,
41
37
  ):
42
- super().__init__()
43
- self.rect = pygame.FRect(*start_pos, 0, 0)
38
+ super().__init__(duration)
39
+ self.rect = pygame.FRect(*start_pos, *size)
44
40
  self.surface = pygame.Surface(size).convert()
41
+ self.velocity = Vector2(start_vel)
45
42
  if color:
46
43
  self.surface.fill(color)
47
- self.z_depth = 1
44
+ self.start()
45
+
46
+ def start(self):
47
+ pass
48
48
 
49
49
  def update(self, dt):
50
50
  super().update(dt)
@@ -52,19 +52,34 @@ class BasicParticle(TimedParticle):
52
52
  self.update_surface()
53
53
 
54
54
  def update_surface(self):
55
- self.surface.set_alpha(255 - int(self.progression * 255))
55
+ self.surface.set_alpha(255 - int(self.timer.get_progression() * 255))
56
+
57
+
58
+ class DirectionalParticle(BasicParticle):
59
+ def start(self):
60
+ self.surface = self.surface.convert_alpha()
61
+ self.original_surface = self.surface.copy()
62
+
63
+ def update_surface(self):
64
+ angle = self.velocity.angle_to(Vector2(1, 0))
65
+ self.surface = pygame.transform.rotate(self.original_surface, angle)
66
+ super().update_surface()
56
67
 
57
68
 
58
- class ParticleManager(bf.Entity):
69
+ class ParticleGenerator(bf.Entity):
59
70
  def __init__(self) -> None:
60
- super().__init__(size=bf.const.RESOLUTION)
71
+ super().__init__((0, 0))
61
72
  self.particles: list[Particle] = []
62
73
 
63
- def get_bounding_box(self):
74
+ def get_debug_outlines(self):
64
75
  for particle in self.particles:
65
- yield particle.rect
76
+ yield (
77
+ particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
78
+ "blue",
79
+ )
80
+ yield (self.rect, "cyan")
66
81
 
67
- def add_particle(self, particle=Particle):
82
+ def add_particle(self, particle):
68
83
  self.particles.append(particle)
69
84
 
70
85
  def clear(self):
@@ -80,19 +95,7 @@ class ParticleManager(bf.Entity):
80
95
  self.particles.remove(p)
81
96
 
82
97
  def draw(self, camera) -> bool:
83
- for p in self.particles:
84
- p.update_surface()
85
98
  camera.surface.fblits(
86
- [
87
- (
88
- p.surface,
89
- tuple(
90
- round(i * self.z_depth)
91
- for i in camera.transpose(self.rect).topleft
92
- ),
93
- )
94
- for p in self.particles
95
- if p.surface
96
- ]
99
+ [(p.surface, camera.world_to_screen(p.rect).center) for p in self.particles]
97
100
  )
98
101
  return len(self.particles)
@@ -0,0 +1,62 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from typing import Self, Iterator, Callable
4
+
5
+ """
6
+ + same render order
7
+ + fblits
8
+ """
9
+
10
+ class RenderGroup(bf.Entity):
11
+ def __init__(self, entity_iterator: Callable[[],Iterator[bf.Entity]], blit_flags: int = 0) -> None:
12
+ super().__init__()
13
+ self.entity_iterator = entity_iterator
14
+ self.set_blit_flags(blit_flags)
15
+ self.set_debug_color("white")
16
+
17
+ def get_debug_outlines(self):
18
+ # yield (self.rect, self.debug_color)
19
+ for e in self.entity_iterator():
20
+ yield from e.get_debug_outlines()
21
+
22
+ def set_parent_scene(self, scene) -> Self:
23
+ self.parent_scene = scene
24
+ for e in self.entity_iterator():
25
+ e.set_parent_scene(scene)
26
+ return self
27
+
28
+ def process_event(self, event: pygame.Event) -> bool:
29
+ """
30
+ Returns bool : True if the method is blocking (no propagation to next children of the scene)
31
+ """
32
+ self.do_process_actions(event)
33
+ res = self.do_handle_event(event)
34
+ if res:
35
+ return res
36
+ for e in self.entity_iterator():
37
+ if e.process_event(event):
38
+ return True
39
+ return False
40
+
41
+ def update(self, dt: float) -> None:
42
+ """
43
+ Update method to be overriden by subclasses of entity
44
+ """
45
+ for e in self.entity_iterator():
46
+ e.update(dt)
47
+ # gen = self.entity_iterator()
48
+ # self.rect = next(gen).rect.unionall([e.rect for e in gen])
49
+
50
+ self.do_update(dt)
51
+ self.do_reset_actions()
52
+
53
+ def draw(self, camera: bf.Camera) -> None:
54
+ """
55
+ Draw the entity onto the camera with coordinate transposing
56
+ """
57
+ if not self.visible:
58
+ return
59
+ fblits_data = (
60
+ (e.surface, (e.rect.x - camera.rect.x,e.rect.y - camera.rect.y)) for e in self.entity_iterator() if camera.rect.colliderect(e.rect)
61
+ )
62
+ camera.surface.fblits(fblits_data, self.blit_flags)
@@ -4,6 +4,7 @@ import pygame
4
4
  import sys
5
5
  import json
6
6
  from .utils import Singleton
7
+
7
8
  if getattr(sys, "frozen", False):
8
9
  # If the application is run as a bundle, the PyInstaller bootloader
9
10
  # extends the sys module by a flag frozen=True and sets the app
@@ -20,41 +21,52 @@ class ResourceManager(metaclass=Singleton):
20
21
  self.sound_cache = {}
21
22
  self.RESOURCE_PATH = "."
22
23
 
23
- def load_dir(self,path)->None:
24
+ def load_dir(self, path) -> None:
24
25
  for root, dirs, files in os.walk(path):
26
+ files = [f for f in files if not f[0] == "."]
27
+ dirs[:] = [d for d in dirs if not d[0] == "."]
25
28
 
26
- files = [f for f in files if not f[0] == '.']
27
- dirs[:] = [d for d in dirs if not d[0] == '.']
28
-
29
29
  for file in files:
30
30
  file_path = os.path.join(root, file)
31
- if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
31
+ if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif")):
32
32
  self.load_image(file_path)
33
-
34
- elif file.lower().endswith(('.mp3', '.wav')):
35
- #AudioManager.load_sound(file_path)
36
- pass
37
- def set_resource_path(self,path: str):
33
+ # print(f"Loaded image : '{file_path}'")
34
+
35
+ elif file.lower().endswith((".mp3", ".wav")):
36
+ bf.AudioManager().load_sound(file.lower().split(".")[0], file_path)
37
+ # print(f"Loaded sound : '{file_path}'")
38
+
39
+ elif file.lower().endswith((".ttf", ".otf")):
40
+ bf.FontManager().load_font(file_path, file.lower().split(".")[0])
41
+ # print(f"Loaded font : '{file_path}'")
42
+
43
+ print(f"Loaded resources in directory : '{path}'")
44
+
45
+ def set_resource_path(self, path: str):
38
46
  self.RESOURCE_PATH = os.path.join(application_path, path)
39
47
  print(f"Resource path : '{self.RESOURCE_PATH}'")
40
-
41
48
 
42
- def get_path(self,path: str):
43
- return os.path.join(self.RESOURCE_PATH, path)
44
-
45
- def load_image(self,path)->None:
49
+ def get_path(self, path: str) -> str:
50
+ # Normalize path separators
51
+ normalized_path = path.replace("/", os.sep).replace("\\", os.sep)
52
+ return os.path.join(self.RESOURCE_PATH, normalized_path)
53
+
54
+ def load_image(self, path) -> None:
46
55
  key = self.get_path(path)
47
- if key in self.convert_image_cache : return
56
+ if key in self.convert_image_cache:
57
+ return
48
58
  self.convert_image_cache[key] = pygame.image.load(path).convert()
49
59
  self.convert_alpha_image_cache[key] = pygame.image.load(path).convert_alpha()
50
- print(f"Loaded image : '{path}'")
51
-
52
- def get_image(self,path,convert_alpha:bool=False):
60
+
61
+ def get_image(self, path, convert_alpha: bool = False) -> pygame.Surface | None:
53
62
  key = self.get_path(path)
54
- return self.convert_alpha_image_cache.get(key,None) if\
55
- convert_alpha else self.convert_image_cache.get(key,None)
56
-
57
- def load_json_from_file(self,path: str) -> dict|None:
63
+ return (
64
+ self.convert_alpha_image_cache.get(key, None)
65
+ if convert_alpha
66
+ else self.convert_image_cache.get(key, None)
67
+ )
68
+
69
+ def load_json_from_file(self, path: str) -> dict | None:
58
70
  try:
59
71
  with open(self.get_path(path), "r") as file:
60
72
  data = json.load(file)
@@ -63,7 +75,7 @@ class ResourceManager(metaclass=Singleton):
63
75
  print(f"File '{path}' not found")
64
76
  return None
65
77
 
66
- def save_json_to_file(self,path: str, data) -> bool:
78
+ def save_json_to_file(self, path: str, data) -> bool:
67
79
  try:
68
80
  with open(self.get_path(path), "w") as file:
69
81
  json.dump(data, file, indent=2)