batframework 1.0.10__py3-none-any.whl → 2.0.0__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 (81) hide show
  1. batFramework/__init__.py +84 -52
  2. batFramework/action.py +280 -252
  3. batFramework/actionContainer.py +105 -38
  4. batFramework/animatedSprite.py +81 -117
  5. batFramework/animation.py +91 -0
  6. batFramework/audioManager.py +156 -85
  7. batFramework/baseScene.py +249 -0
  8. batFramework/camera.py +245 -123
  9. batFramework/constants.py +57 -75
  10. batFramework/cutscene.py +239 -119
  11. batFramework/cutsceneManager.py +34 -0
  12. batFramework/drawable.py +107 -0
  13. batFramework/dynamicEntity.py +30 -23
  14. batFramework/easingController.py +58 -0
  15. batFramework/entity.py +130 -123
  16. batFramework/enums.py +171 -0
  17. batFramework/fontManager.py +65 -0
  18. batFramework/gui/__init__.py +28 -14
  19. batFramework/gui/animatedLabel.py +90 -0
  20. batFramework/gui/button.py +18 -84
  21. batFramework/gui/clickableWidget.py +244 -0
  22. batFramework/gui/collapseContainer.py +98 -0
  23. batFramework/gui/constraints/__init__.py +1 -0
  24. batFramework/gui/constraints/constraints.py +1066 -0
  25. batFramework/gui/container.py +220 -49
  26. batFramework/gui/debugger.py +140 -47
  27. batFramework/gui/draggableWidget.py +63 -0
  28. batFramework/gui/image.py +61 -23
  29. batFramework/gui/indicator.py +116 -40
  30. batFramework/gui/interactiveWidget.py +243 -22
  31. batFramework/gui/label.py +147 -110
  32. batFramework/gui/layout.py +442 -81
  33. batFramework/gui/meter.py +155 -0
  34. batFramework/gui/radioButton.py +43 -0
  35. batFramework/gui/root.py +228 -60
  36. batFramework/gui/scrollingContainer.py +282 -0
  37. batFramework/gui/selector.py +232 -0
  38. batFramework/gui/shape.py +286 -86
  39. batFramework/gui/slider.py +353 -0
  40. batFramework/gui/style.py +10 -0
  41. batFramework/gui/styleManager.py +49 -0
  42. batFramework/gui/syncedVar.py +43 -0
  43. batFramework/gui/textInput.py +331 -0
  44. batFramework/gui/textWidget.py +308 -0
  45. batFramework/gui/toggle.py +140 -62
  46. batFramework/gui/tooltip.py +35 -0
  47. batFramework/gui/widget.py +546 -307
  48. batFramework/manager.py +131 -50
  49. batFramework/particle.py +118 -0
  50. batFramework/propertyEaser.py +79 -0
  51. batFramework/renderGroup.py +34 -0
  52. batFramework/resourceManager.py +130 -0
  53. batFramework/scene.py +31 -226
  54. batFramework/sceneLayer.py +134 -0
  55. batFramework/sceneManager.py +200 -165
  56. batFramework/scrollingSprite.py +115 -0
  57. batFramework/sprite.py +46 -0
  58. batFramework/stateMachine.py +49 -51
  59. batFramework/templates/__init__.py +2 -0
  60. batFramework/templates/character.py +15 -0
  61. batFramework/templates/controller.py +158 -0
  62. batFramework/templates/stateMachine.py +39 -0
  63. batFramework/tileset.py +46 -0
  64. batFramework/timeManager.py +213 -0
  65. batFramework/transition.py +162 -157
  66. batFramework/triggerZone.py +22 -22
  67. batFramework/utils.py +306 -184
  68. {batframework-1.0.10.dist-info → batframework-2.0.0.dist-info}/LICENSE +1 -1
  69. {batframework-1.0.10.dist-info → batframework-2.0.0.dist-info}/METADATA +3 -4
  70. batframework-2.0.0.dist-info/RECORD +72 -0
  71. batFramework/cutsceneBlocks.py +0 -176
  72. batFramework/debugger.py +0 -48
  73. batFramework/easing.py +0 -71
  74. batFramework/gui/constraints.py +0 -204
  75. batFramework/gui/frame.py +0 -19
  76. batFramework/particles.py +0 -77
  77. batFramework/time.py +0 -75
  78. batFramework/transitionManager.py +0 -0
  79. batframework-1.0.10.dist-info/RECORD +0 -43
  80. {batframework-1.0.10.dist-info → batframework-2.0.0.dist-info}/WHEEL +0 -0
  81. {batframework-1.0.10.dist-info → batframework-2.0.0.dist-info}/top_level.txt +0 -0
batFramework/manager.py CHANGED
@@ -1,50 +1,131 @@
1
- import batFramework as bf
2
- import pygame
3
- import random
4
-
5
- class Manager(bf.SceneManager):
6
- 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)
11
- self._clock: pygame.Clock = pygame.Clock()
12
- super().__init__(*initial_scene_list)
13
- self.set_sharedVar("clock", self._clock)
14
- self.do_init()
15
-
16
- @staticmethod
17
- def set_icon(path: str):
18
- surf = pygame.image.load(bf.utils.get_path(path)).convert_alpha()
19
- pygame.display.set_icon(surf)
20
-
21
- def get_fps(self):
22
- return self._clock.get_fps()
23
-
24
- def do_init(self):
25
- pass
26
-
27
- def stop(self)->None:
28
- self._running = False
29
-
30
- def run(self):
31
- self._running = True
32
- dt: float = 0
33
- while self._running:
34
- 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))
40
- self.process_event(event)
41
- # update
42
- dt = self._clock.tick(bf.const.FPS if not bf.const.VSYNC else 0) / 1000
43
- dt = min(dt, 0.02)
44
- self._cutsceneManager.update(dt)
45
- self._timeManager.update()
46
- self.update(dt)
47
- # render
48
- self.draw(self._screen)
49
- pygame.display.flip()
50
- pygame.quit()
1
+ import batFramework as bf
2
+ from batFramework import const
3
+ import pygame
4
+ import asyncio
5
+
6
+
7
+ class Manager(bf.SceneManager):
8
+ def __init__(self, *initial_scenes) -> None:
9
+ super().__init__()
10
+ self.debug_mode: bf.enums.debugMode = bf.debugMode.HIDDEN
11
+ self.screen: pygame.Surface | None = bf.const.SCREEN
12
+ self.timeManager = bf.TimeManager()
13
+ self.cutsceneManager = bf.CutsceneManager()
14
+ self.cutsceneManager.set_manager(self)
15
+ self.clock: pygame.Clock = pygame.Clock()
16
+ self.is_async_running : bool = False
17
+ self.running = False
18
+ pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
19
+ bf.ResourceManager().set_sharedVar("clock", self.clock)
20
+ bf.ResourceManager().set_sharedVar("debug_mode", self.debug_mode)
21
+
22
+ self.do_pre_init()
23
+ if initial_scenes:
24
+ self.init_scenes(*initial_scenes)
25
+ self.do_init()
26
+
27
+ @staticmethod
28
+ def set_icon(path: str) -> None:
29
+ surf = pygame.image.load(bf.ResourceManager().get_path(path)).convert_alpha()
30
+ pygame.display.set_icon(surf)
31
+
32
+ def print_status(self):
33
+ """
34
+ Print detailed information about the current state of the scenes, shared variables,
35
+ and additional timers managed by the subclass.
36
+ """
37
+ # Call the parent class's print_status method to include its information
38
+ super().print_status()
39
+ # Add the timers information in a cohesive manner
40
+ print("\n" + "=" * 50)
41
+ print(" TIMERS".center(50))
42
+ print("=" * 50)
43
+ # Print the timers information
44
+ print(self.timeManager)
45
+ # End with a visual separator
46
+ print("=" * 50 + "\n")
47
+
48
+
49
+ def get_fps(self) -> float:
50
+ return self.clock.get_fps()
51
+
52
+ def do_init(self) -> None:
53
+ pass
54
+
55
+ def do_pre_init(self) -> None:
56
+ pass
57
+
58
+ def stop(self) -> None:
59
+ self.running = False
60
+
61
+ def process_event(self, event: pygame.Event):
62
+ event.consumed = False
63
+ keys = pygame.key.get_pressed()
64
+ if (
65
+ bf.const.ALLOW_DEBUG and
66
+ keys[pygame.K_LCTRL]
67
+ and keys[pygame.K_LSHIFT]
68
+ and event.type == pygame.KEYDOWN
69
+ ):
70
+ if event.key == pygame.K_d:
71
+ self.cycle_debug_mode()
72
+ return
73
+ if event.key == pygame.K_p:
74
+ self.print_status()
75
+ return
76
+ self.cutsceneManager.process_event(event)
77
+ if event.type == pygame.VIDEORESIZE and not (bf.const.FLAGS & pygame.SCALED):
78
+ bf.const.set_resolution((event.w, event.h))
79
+
80
+ if event.consumed: return
81
+
82
+ super().process_event(event)
83
+ if not event.consumed:
84
+ if event.type == pygame.QUIT:
85
+ self.running = False
86
+
87
+ def update(self, dt: float) -> None:
88
+ self.timeManager.update(dt)
89
+ self.cutsceneManager.update(dt)
90
+ super().update(dt)
91
+
92
+
93
+ async def run_async(self):
94
+ if len(self.scenes) == 0:
95
+ raise Exception("Manager can't start without scenes")
96
+ if self.running:
97
+ raise Exception("Error : Already running")
98
+ self.is_async_running = True
99
+ self.running = True
100
+ dt: float = 0
101
+ while self.running:
102
+ for event in pygame.event.get():
103
+ self.process_event(event)
104
+ # update
105
+ self.update(dt)
106
+ # render
107
+ self.draw(self.screen)
108
+ pygame.display.flip()
109
+ dt = self.clock.tick(bf.const.FPS) / 1000
110
+ dt = min(dt, 0.02) # dirty fix for dt being too high when window not focused for a long time
111
+ await asyncio.sleep(0)
112
+ pygame.quit()
113
+
114
+ def run(self) -> None:
115
+ if len(self.scenes) == 0:
116
+ raise Exception("Manager can't start without scenes")
117
+ if self.running:
118
+ raise Exception("Error : Already running")
119
+ self.running = True
120
+ dt: float = 0
121
+ while self.running:
122
+ for event in pygame.event.get():
123
+ self.process_event(event)
124
+ # update
125
+ self.update(dt)
126
+ # render
127
+ self.draw(self.screen)
128
+ pygame.display.flip()
129
+ dt = self.clock.tick(bf.const.FPS) / 1000
130
+ dt = min(dt, 0.02) # fix for dt being too high when window not focused for a long time
131
+ pygame.quit()
@@ -0,0 +1,118 @@
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
+ self.generator = None
11
+
12
+ def do_when_added(self):
13
+ pass
14
+
15
+ def update(self, dt):
16
+ pass
17
+
18
+ def kill(self):
19
+ self.dead = True
20
+
21
+ def update_surface(self):
22
+ pass
23
+
24
+
25
+ class TimedParticle(Particle):
26
+ def __init__(self, duration):
27
+ super().__init__()
28
+ self.duration = duration
29
+
30
+ def do_when_added(self):
31
+ if self.generator and self.generator.parent_scene:
32
+ self.timer = bf.SceneTimer(
33
+ self.duration, end_callback=self.kill,
34
+ scene_name=self.generator.parent_scene.name).start()
35
+ else:
36
+ self.timer = bf.Timer(self.duration, end_callback=self.kill).start()
37
+
38
+
39
+ class BasicParticle(TimedParticle):
40
+ def __init__(
41
+ self,
42
+ start_pos: tuple[float, float],
43
+ start_vel: tuple[float, float],
44
+ duration=1,
45
+ color=None,
46
+ size: tuple[int, int] = (4, 4),
47
+ *args,
48
+ **kwargs,
49
+ ):
50
+ super().__init__(duration)
51
+ self.rect = pygame.FRect(0,0, *size)
52
+ self.rect.center = start_pos
53
+ self.surface = pygame.Surface(size)
54
+ self.velocity = Vector2(start_vel)
55
+ if color:
56
+ self.surface.fill(color)
57
+ self.start()
58
+
59
+ def start(self):
60
+ pass
61
+
62
+ def update(self, dt):
63
+ super().update(dt)
64
+ self.rect.center += self.velocity * dt
65
+ self.update_surface()
66
+
67
+ def update_surface(self):
68
+ self.surface.set_alpha(255 - int(self.timer.get_progression() * 255))
69
+
70
+
71
+ class DirectionalParticle(BasicParticle):
72
+ def start(self):
73
+ self.original_surface = self.surface.copy()
74
+
75
+ def update_surface(self):
76
+ angle = self.velocity.angle_to(Vector2(1, 0))
77
+ self.surface = pygame.transform.rotate(self.original_surface, angle)
78
+ super().update_surface()
79
+
80
+
81
+ class ParticleGenerator(bf.Drawable):
82
+ def __init__(self) -> None:
83
+ super().__init__((0, 0))
84
+ self.particles: list[Particle] = []
85
+ self.count = 0
86
+
87
+ def get_debug_outlines(self):
88
+ return
89
+ for particle in self.particles:
90
+ yield (
91
+ particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
92
+ "blue",
93
+ )
94
+ yield (self.rect, "cyan")
95
+
96
+ def add_particle(self, particle:Particle):
97
+ particle.generator = self
98
+ particle.do_when_added()
99
+ self.particles.append(particle)
100
+ self.count += 1
101
+
102
+ def clear(self):
103
+ self.particles = []
104
+ self.count = 0
105
+ def update(self, dt: float):
106
+ particles_to_remove = []
107
+ for particle in self.particles:
108
+ particle.update(dt)
109
+ if particle.dead:
110
+ particles_to_remove.append(particle)
111
+ for p in particles_to_remove:
112
+ self.particles.remove(p)
113
+ self.count -= 1
114
+
115
+ def draw(self, camera) -> None:
116
+ camera.surface.fblits(
117
+ [(p.surface, camera.world_to_screen(p.rect)) for p in self.particles]
118
+ )
@@ -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
@@ -0,0 +1,34 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from typing import Callable, Iterator
4
+
5
+
6
+ class RenderGroup(bf.Drawable):
7
+ def __init__(
8
+ self, entity_iterator: Callable[[], Iterator[bf.Drawable]], blit_flags: int = 0
9
+ ) -> None:
10
+ super().__init__()
11
+ self.entity_iterator = entity_iterator
12
+ self.blit_flags = blit_flags
13
+ self.set_debug_color("white")
14
+
15
+ def draw(self, camera: bf.Camera) -> None:
16
+ if not self.visible:
17
+ return
18
+
19
+ fblits_data = []
20
+ for e in self.entity_iterator():
21
+ if not getattr(e, "drawn_by_group", False):
22
+ # Set flag to skip their individual draw call
23
+ e.drawn_by_group = True
24
+
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
+ )
29
+
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()
@@ -0,0 +1,130 @@
1
+ import batFramework as bf
2
+ import os
3
+ import pygame
4
+ import sys
5
+ import json
6
+ from typing import Any, Callable
7
+ from .utils import Singleton
8
+ import asyncio
9
+
10
+
11
+ if getattr(sys, "frozen", False):
12
+ # If the application is run as a bundle, the PyInstaller bootloader
13
+ # extends the sys module by a flag frozen=True and sets the app
14
+ # path into variable _MEIPASS'.
15
+ application_path = sys._MEIPASS
16
+ else:
17
+ application_path = os.getcwd()
18
+
19
+
20
+ class ResourceManager(metaclass=Singleton):
21
+ def __init__(self):
22
+ self.shared_variables: dict[str, Any] = {}
23
+ self.convert_image_cache = {}
24
+ self.convert_alpha_image_cache = {}
25
+ self.sound_cache = {}
26
+ self.RESOURCE_PATH = "."
27
+ self.loading_thread = None
28
+
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
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("__"))]
46
+ for file in files:
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
+
52
+ if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif")):
53
+ self.load_image(file_path)
54
+ elif file.lower().endswith((".mp3", ".wav", ".ogg")):
55
+ bf.AudioManager().load_sound(file.split(".")[0], file_path)
56
+ elif file.lower().endswith((".ttf", ".otf")):
57
+ bf.FontManager().load_font(file_path, file.split(".")[0])
58
+
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
+
66
+
67
+ def set_resource_path(self, path: str):
68
+ self.RESOURCE_PATH = os.path.join(application_path, path)
69
+ print(f"Resource path : '{self.RESOURCE_PATH}'")
70
+
71
+ def get_path(self, path: str) -> str:
72
+ # Normalize path separators
73
+ normalized_path = path.replace("/", os.sep).replace("\\", os.sep)
74
+ return os.path.join(self.RESOURCE_PATH, normalized_path)
75
+
76
+ def load_image(self, path) -> None:
77
+ key = self.get_path(path)
78
+ if key in self.convert_image_cache:
79
+ return
80
+ self.convert_image_cache[key] = pygame.image.load(path).convert()
81
+ self.convert_alpha_image_cache[key] = pygame.image.load(path).convert_alpha()
82
+
83
+
84
+ def get_image(self, path, convert_alpha: bool = False) -> pygame.Surface | None:
85
+ key = self.get_path(path)
86
+ return (
87
+ self.convert_alpha_image_cache.get(key, None)
88
+ if convert_alpha
89
+ else self.convert_image_cache.get(key, None)
90
+ )
91
+
92
+ def load_json_from_file(self, path: str) -> dict | None:
93
+ try:
94
+ with open(self.get_path(path), "r") as file:
95
+ data = json.load(file)
96
+ return data
97
+ except FileNotFoundError:
98
+ print(f"File '{path}' not found")
99
+ return None
100
+
101
+ def save_json_to_file(self, path: str, data) -> bool:
102
+ try:
103
+ with open(self.get_path(path), "w") as file:
104
+ json.dump(data, file, indent=2)
105
+ return True
106
+ except FileNotFoundError:
107
+ return False
108
+
109
+
110
+ def set_sharedVar(self, name, value) -> bool:
111
+ """
112
+ Set a shared variable of any type. This will be accessible (read/write) from any scene
113
+ """
114
+ self.shared_variables[name] = value
115
+ return True
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
+
126
+ def get_sharedVar(self, name, error_value=None):
127
+ """
128
+ Get a shared variable
129
+ """
130
+ return self.shared_variables.get(name, error_value)