batframework 1.0.8a2__py3-none-any.whl → 1.0.8a3__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 (65) hide show
  1. batFramework/__init__.py +53 -50
  2. batFramework/action.py +126 -99
  3. batFramework/actionContainer.py +53 -9
  4. batFramework/animatedSprite.py +115 -65
  5. batFramework/audioManager.py +69 -26
  6. batFramework/camera.py +259 -69
  7. batFramework/constants.py +16 -54
  8. batFramework/cutscene.py +36 -29
  9. batFramework/cutsceneBlocks.py +37 -42
  10. batFramework/dynamicEntity.py +9 -7
  11. batFramework/easingController.py +58 -0
  12. batFramework/entity.py +48 -97
  13. batFramework/enums.py +113 -0
  14. batFramework/fontManager.py +65 -0
  15. batFramework/gui/__init__.py +10 -2
  16. batFramework/gui/button.py +9 -78
  17. batFramework/gui/clickableWidget.py +219 -0
  18. batFramework/gui/constraints/__init__.py +1 -0
  19. batFramework/gui/constraints/constraints.py +590 -0
  20. batFramework/gui/container.py +174 -32
  21. batFramework/gui/debugger.py +131 -43
  22. batFramework/gui/dialogueBox.py +99 -0
  23. batFramework/gui/draggableWidget.py +40 -0
  24. batFramework/gui/image.py +54 -18
  25. batFramework/gui/indicator.py +38 -21
  26. batFramework/gui/interactiveWidget.py +177 -13
  27. batFramework/gui/label.py +288 -74
  28. batFramework/gui/layout.py +219 -60
  29. batFramework/gui/meter.py +71 -0
  30. batFramework/gui/radioButton.py +84 -0
  31. batFramework/gui/root.py +128 -38
  32. batFramework/gui/shape.py +253 -57
  33. batFramework/gui/slider.py +246 -0
  34. batFramework/gui/style.py +10 -0
  35. batFramework/gui/styleManager.py +48 -0
  36. batFramework/gui/textInput.py +137 -0
  37. batFramework/gui/toggle.py +115 -51
  38. batFramework/gui/widget.py +329 -254
  39. batFramework/manager.py +40 -19
  40. batFramework/object.py +114 -0
  41. batFramework/particle.py +101 -0
  42. batFramework/renderGroup.py +67 -0
  43. batFramework/resourceManager.py +84 -0
  44. batFramework/scene.py +242 -114
  45. batFramework/sceneManager.py +145 -107
  46. batFramework/scrollingSprite.py +115 -0
  47. batFramework/sprite.py +51 -0
  48. batFramework/stateMachine.py +2 -2
  49. batFramework/tileset.py +46 -0
  50. batFramework/time.py +117 -57
  51. batFramework/transition.py +184 -126
  52. batFramework/utils.py +31 -156
  53. batframework-1.0.8a3.dist-info/LICENCE +21 -0
  54. batframework-1.0.8a3.dist-info/METADATA +55 -0
  55. batframework-1.0.8a3.dist-info/RECORD +58 -0
  56. batFramework/debugger.py +0 -48
  57. batFramework/easing.py +0 -71
  58. batFramework/gui/constraints.py +0 -204
  59. batFramework/gui/frame.py +0 -19
  60. batFramework/particles.py +0 -77
  61. batFramework/transitionManager.py +0 -0
  62. batframework-1.0.8a2.dist-info/METADATA +0 -58
  63. batframework-1.0.8a2.dist-info/RECORD +0 -42
  64. {batframework-1.0.8a2.dist-info → batframework-1.0.8a3.dist-info}/WHEEL +0 -0
  65. {batframework-1.0.8a2.dist-info → batframework-1.0.8a3.dist-info}/top_level.txt +0 -0
batFramework/manager.py CHANGED
@@ -2,49 +2,70 @@ import batFramework as bf
2
2
  import pygame
3
3
  import random
4
4
 
5
+
5
6
  class Manager(bf.SceneManager):
6
7
  def __init__(self, *initial_scene_list) -> None:
7
- random.seed("random")
8
- self._screen: pygame.Surface = bf.const.SCREEN
9
- self._timeManager = bf.Time()
10
- self._cutsceneManager = bf.CutsceneManager(self)
8
+ # random.seed("random")
9
+ super().__init__()
10
+ self._screen: pygame.Surface | None = bf.const.SCREEN
11
+ self._timeManager = bf.TimeManager()
12
+ self._cutsceneManager = bf.CutsceneManager()
13
+ self._cutsceneManager.set_manager(self)
11
14
  self._clock: pygame.Clock = pygame.Clock()
12
- super().__init__(*initial_scene_list)
15
+ pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
16
+ self.do_pre_init()
17
+ self.init_scenes(*initial_scene_list)
13
18
  self.set_sharedVar("clock", self._clock)
14
19
  self.do_init()
15
20
 
16
21
  @staticmethod
17
- def set_icon(path: str):
18
- surf = pygame.image.load(bf.utils.get_path(path)).convert_alpha()
22
+ def set_icon(path: str) -> None:
23
+ surf = pygame.image.load(bf.ResourceManager().get_path(path)).convert_alpha()
19
24
  pygame.display.set_icon(surf)
20
25
 
21
- def get_fps(self):
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:
22
35
  return self._clock.get_fps()
23
36
 
24
- def do_init(self):
37
+ def do_init(self) -> None:
38
+ pass
39
+
40
+ def do_pre_init(self) -> None:
25
41
  pass
26
42
 
27
- def stop(self)->None:
43
+ def stop(self) -> None:
28
44
  self._running = False
29
45
 
30
- def run(self):
46
+ def run(self) -> None:
31
47
  self._running = True
32
48
  dt: float = 0
33
49
  while self._running:
34
50
  for event in pygame.event.get():
35
- if event.type == pygame.QUIT:
36
- self._running = False
37
- break
38
- if event.type == pygame.VIDEORESIZE:
39
- bf.const.set_resolution((event.w,event.h))
51
+ event.consumed = False
40
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))
41
61
  # update
42
- dt = self._clock.tick(bf.const.FPS if not bf.const.VSYNC else 0) / 1000
43
- dt = min(dt, 0.02)
62
+ dt = self._clock.tick(bf.const.FPS) / 1000
63
+ # dt = min(dt, 0.02) dirty fix for dt being too high when window not focused for a long time
64
+ self._timeManager.update(dt)
44
65
  self._cutsceneManager.update(dt)
45
- self._timeManager.update()
46
66
  self.update(dt)
47
67
  # render
68
+ self._screen.fill((0, 0, 0))
48
69
  self.draw(self._screen)
49
70
  pygame.display.flip()
50
71
  pygame.quit()
batFramework/object.py ADDED
@@ -0,0 +1,114 @@
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:
45
+ return self
46
+ if self.parent_scene is not None:
47
+ self.do_when_removed()
48
+ self.parent_scene = scene
49
+ if scene is not None:
50
+ self.do_when_added()
51
+ return self
52
+
53
+ def do_when_added(self):
54
+ pass
55
+
56
+ def do_when_removed(self):
57
+ pass
58
+
59
+ def set_uid(self, uid: int) -> Self:
60
+ self.uid = uid
61
+ return self
62
+
63
+ def add_tags(self, *tags) -> Self:
64
+ for tag in tags:
65
+ if tag not in self.tags:
66
+ self.tags.append(tag)
67
+ self.tags.sort()
68
+ return self
69
+
70
+ def remove_tags(self, *tags):
71
+ self.tags = [tag for tag in self.tags if tag not in tags]
72
+
73
+ def has_tags(self, *tags) -> bool:
74
+ return all(tag in self.tags for tag in tags)
75
+
76
+ def get_tags(self) -> list[str]:
77
+ return self.tags
78
+
79
+ def process_event(self, event: pygame.Event) -> bool:
80
+ """
81
+ Returns bool : True if the method is blocking (no propagation to next object of the scene)
82
+ """
83
+ if event.consumed:
84
+ return
85
+ self.do_process_actions(event)
86
+ self.do_handle_event(event)
87
+
88
+ def do_process_actions(self, event: pygame.Event) -> None:
89
+ """
90
+ Process entity actions you may have set
91
+ """
92
+
93
+ def do_reset_actions(self) -> None:
94
+ """
95
+ Reset entity actions you may have set
96
+ """
97
+
98
+ def do_handle_event(self, event: pygame.Event):
99
+ """
100
+ Handle specific events with no action support
101
+ """
102
+ return False
103
+
104
+ def update(self, dt: float) -> None:
105
+ """
106
+ Update method to be overriden by subclasses of object (must call do_update and do_reset_actions)
107
+ """
108
+ self.do_update(dt)
109
+ self.do_reset_actions()
110
+
111
+ def do_update(self, dt: float) -> None:
112
+ """
113
+ Update method to be overriden for specific behavior by the end user
114
+ """
@@ -0,0 +1,101 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from pygame.math import Vector2
4
+
5
+
6
+ class Particle:
7
+ def __init__(self, *args, **kwargs):
8
+ self.dead = False
9
+ self.surface = None
10
+
11
+ def update(self, dt):
12
+ pass
13
+
14
+ def kill(self):
15
+ self.dead = True
16
+
17
+ def update_surface(self):
18
+ pass
19
+
20
+
21
+ class TimedParticle(Particle):
22
+ def __init__(self, duration):
23
+ super().__init__()
24
+ self.timer = bf.Timer(duration, end_callback=self.kill).start()
25
+
26
+
27
+ class BasicParticle(TimedParticle):
28
+ def __init__(
29
+ self,
30
+ start_pos: tuple[float, float],
31
+ start_vel: tuple[float, float],
32
+ duration=1,
33
+ color=None,
34
+ size: tuple[int, int] = (4, 4),
35
+ *args,
36
+ **kwargs,
37
+ ):
38
+ super().__init__(duration)
39
+ self.rect = pygame.FRect(*start_pos, *size)
40
+ self.surface = pygame.Surface(size).convert()
41
+ self.velocity = Vector2(start_vel)
42
+ if color:
43
+ self.surface.fill(color)
44
+ self.start()
45
+
46
+ def start(self):
47
+ pass
48
+
49
+ def update(self, dt):
50
+ super().update(dt)
51
+ self.rect.center += self.velocity * dt
52
+ self.update_surface()
53
+
54
+ def update_surface(self):
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()
67
+
68
+
69
+ class ParticleGenerator(bf.Entity):
70
+ def __init__(self) -> None:
71
+ super().__init__((0, 0))
72
+ self.particles: list[Particle] = []
73
+
74
+ def get_debug_outlines(self):
75
+ for particle in self.particles:
76
+ yield (
77
+ particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
78
+ "blue",
79
+ )
80
+ yield (self.rect, "cyan")
81
+
82
+ def add_particle(self, particle):
83
+ self.particles.append(particle)
84
+
85
+ def clear(self):
86
+ self.particles = []
87
+
88
+ def update(self, dt: float):
89
+ particles_to_remove = []
90
+ for particle in self.particles:
91
+ particle.update(dt)
92
+ if particle.dead:
93
+ particles_to_remove.append(particle)
94
+ for p in particles_to_remove:
95
+ self.particles.remove(p)
96
+
97
+ def draw(self, camera) -> bool:
98
+ camera.surface.fblits(
99
+ [(p.surface, camera.world_to_screen(p.rect).center) for p in self.particles]
100
+ )
101
+ return len(self.particles)
@@ -0,0 +1,67 @@
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
+
11
+ class RenderGroup(bf.Entity):
12
+ def __init__(
13
+ self, entity_iterator: Callable[[], Iterator[bf.Entity]], blit_flags: int = 0
14
+ ) -> None:
15
+ super().__init__()
16
+ self.entity_iterator = entity_iterator
17
+ self.set_blit_flags(blit_flags)
18
+ self.set_debug_color("white")
19
+
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
43
+
44
+ def update(self, dt: float) -> None:
45
+ """
46
+ Update method to be overriden by subclasses of entity
47
+ """
48
+ 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])
52
+
53
+ self.do_update(dt)
54
+ self.do_reset_actions()
55
+
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
+ camera.surface.fblits(fblits_data, self.blit_flags)
@@ -0,0 +1,84 @@
1
+ import batFramework as bf
2
+ import os
3
+ import pygame
4
+ import sys
5
+ import json
6
+ from .utils import Singleton
7
+
8
+ if getattr(sys, "frozen", False):
9
+ # If the application is run as a bundle, the PyInstaller bootloader
10
+ # extends the sys module by a flag frozen=True and sets the app
11
+ # path into variable _MEIPASS'.
12
+ application_path = sys._MEIPASS
13
+ else:
14
+ application_path = os.getcwd()
15
+
16
+
17
+ class ResourceManager(metaclass=Singleton):
18
+ def __init__(self):
19
+ self.convert_image_cache = {}
20
+ self.convert_alpha_image_cache = {}
21
+ self.sound_cache = {}
22
+ self.RESOURCE_PATH = "."
23
+
24
+ def load_dir(self, path) -> None:
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] == "."]
28
+
29
+ for file in files:
30
+ file_path = os.path.join(root, file)
31
+ if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif")):
32
+ self.load_image(file_path)
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):
46
+ self.RESOURCE_PATH = os.path.join(application_path, path)
47
+ print(f"Resource path : '{self.RESOURCE_PATH}'")
48
+
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:
55
+ key = self.get_path(path)
56
+ if key in self.convert_image_cache:
57
+ return
58
+ self.convert_image_cache[key] = pygame.image.load(path).convert()
59
+ self.convert_alpha_image_cache[key] = pygame.image.load(path).convert_alpha()
60
+
61
+ def get_image(self, path, convert_alpha: bool = False) -> pygame.Surface | None:
62
+ key = self.get_path(path)
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:
70
+ try:
71
+ with open(self.get_path(path), "r") as file:
72
+ data = json.load(file)
73
+ return data
74
+ except FileNotFoundError:
75
+ print(f"File '{path}' not found")
76
+ return None
77
+
78
+ def save_json_to_file(self, path: str, data) -> bool:
79
+ try:
80
+ with open(self.get_path(path), "w") as file:
81
+ json.dump(data, file, indent=2)
82
+ return True
83
+ except FileNotFoundError:
84
+ return False