batframework 1.0.8a9__py3-none-any.whl → 1.0.8a10__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 (70) hide show
  1. batFramework/__init__.py +68 -51
  2. batFramework/action.py +126 -99
  3. batFramework/actionContainer.py +53 -9
  4. batFramework/animatedSprite.py +141 -82
  5. batFramework/audioManager.py +69 -26
  6. batFramework/camera.py +259 -69
  7. batFramework/character.py +27 -0
  8. batFramework/constants.py +16 -54
  9. batFramework/cutscene.py +39 -29
  10. batFramework/cutsceneBlocks.py +36 -43
  11. batFramework/dynamicEntity.py +18 -9
  12. batFramework/easingController.py +58 -0
  13. batFramework/entity.py +48 -97
  14. batFramework/enums.py +113 -0
  15. batFramework/fontManager.py +65 -0
  16. batFramework/gui/__init__.py +10 -2
  17. batFramework/gui/button.py +9 -78
  18. batFramework/gui/clickableWidget.py +220 -0
  19. batFramework/gui/constraints/__init__.py +1 -0
  20. batFramework/gui/constraints/constraints.py +815 -0
  21. batFramework/gui/container.py +174 -32
  22. batFramework/gui/debugger.py +131 -43
  23. batFramework/gui/dialogueBox.py +99 -0
  24. batFramework/gui/draggableWidget.py +40 -0
  25. batFramework/gui/image.py +56 -20
  26. batFramework/gui/indicator.py +38 -21
  27. batFramework/gui/interactiveWidget.py +192 -13
  28. batFramework/gui/label.py +309 -74
  29. batFramework/gui/layout.py +231 -63
  30. batFramework/gui/meter.py +74 -0
  31. batFramework/gui/radioButton.py +84 -0
  32. batFramework/gui/root.py +134 -38
  33. batFramework/gui/shape.py +237 -57
  34. batFramework/gui/slider.py +240 -0
  35. batFramework/gui/style.py +10 -0
  36. batFramework/gui/styleManager.py +48 -0
  37. batFramework/gui/textInput.py +247 -0
  38. batFramework/gui/toggle.py +101 -51
  39. batFramework/gui/widget.py +358 -250
  40. batFramework/manager.py +52 -19
  41. batFramework/object.py +123 -0
  42. batFramework/particle.py +115 -0
  43. batFramework/renderGroup.py +67 -0
  44. batFramework/resourceManager.py +100 -0
  45. batFramework/scene.py +281 -123
  46. batFramework/sceneManager.py +178 -116
  47. batFramework/scrollingSprite.py +114 -0
  48. batFramework/sprite.py +51 -0
  49. batFramework/stateMachine.py +11 -8
  50. batFramework/templates/__init__.py +2 -0
  51. batFramework/templates/character.py +44 -0
  52. batFramework/templates/states.py +166 -0
  53. batFramework/tileset.py +46 -0
  54. batFramework/time.py +145 -58
  55. batFramework/transition.py +195 -124
  56. batFramework/triggerZone.py +1 -1
  57. batFramework/utils.py +112 -147
  58. batframework-1.0.8a10.dist-info/LICENCE +21 -0
  59. batframework-1.0.8a10.dist-info/METADATA +43 -0
  60. batframework-1.0.8a10.dist-info/RECORD +62 -0
  61. batFramework/debugger.py +0 -48
  62. batFramework/easing.py +0 -71
  63. batFramework/gui/constraints.py +0 -204
  64. batFramework/gui/frame.py +0 -19
  65. batFramework/particles.py +0 -77
  66. batFramework/transitionManager.py +0 -0
  67. batframework-1.0.8a9.dist-info/METADATA +0 -53
  68. batframework-1.0.8a9.dist-info/RECORD +0 -42
  69. {batframework-1.0.8a9.dist-info → batframework-1.0.8a10.dist-info}/WHEEL +0 -0
  70. {batframework-1.0.8a9.dist-info → batframework-1.0.8a10.dist-info}/top_level.txt +0 -0
batFramework/manager.py CHANGED
@@ -2,49 +2,82 @@ 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
+ """
28
+ Print detailed information about the current state of the scenes, shared variables,
29
+ and additional timers managed by the subclass.
30
+ """
31
+ # Call the parent class's print_status method to include its information
32
+ super().print_status()
33
+
34
+ # Add the timers information in a cohesive manner
35
+ print("\n" + "=" * 50)
36
+ print(" TIMERS".center(50))
37
+ print("=" * 50)
38
+
39
+ # Print the timers information
40
+ print(self._timeManager)
41
+
42
+ # End with a visual separator
43
+ print("=" * 50 + "\n")
44
+
45
+
46
+ def get_fps(self) -> float:
22
47
  return self._clock.get_fps()
23
48
 
24
- def do_init(self):
49
+ def do_init(self) -> None:
50
+ pass
51
+
52
+ def do_pre_init(self) -> None:
25
53
  pass
26
54
 
27
- def stop(self)->None:
55
+ def stop(self) -> None:
28
56
  self._running = False
29
57
 
30
- def run(self):
58
+ def run(self) -> None:
31
59
  self._running = True
32
60
  dt: float = 0
33
61
  while self._running:
34
62
  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))
63
+ event.consumed = False
40
64
  self.process_event(event)
65
+ if not event.consumed:
66
+ if event.type == pygame.QUIT:
67
+ self._running = False
68
+ break
69
+ if event.type == pygame.VIDEORESIZE and not (
70
+ bf.const.FLAGS & pygame.SCALED
71
+ ):
72
+ bf.const.set_resolution((event.w, event.h))
41
73
  # update
42
- dt = self._clock.tick(bf.const.FPS if not bf.const.VSYNC else 0) / 1000
43
- dt = min(dt, 0.02)
74
+ dt = self._clock.tick(bf.const.FPS) / 1000
75
+ # dt = min(dt, 0.02) dirty fix for dt being too high when window not focused for a long time
76
+ self._timeManager.update(dt)
44
77
  self._cutsceneManager.update(dt)
45
- self._timeManager.update()
46
78
  self.update(dt)
47
79
  # render
80
+ self._screen.fill((0, 0, 0))
48
81
  self.draw(self._screen)
49
82
  pygame.display.flip()
50
83
  pygame.quit()
batFramework/object.py ADDED
@@ -0,0 +1,123 @@
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
+ __count = 0
12
+ __available_uid = set()
13
+ __used_uid = set()
14
+
15
+ def __init__(self) -> None:
16
+ self.rect = pygame.FRect(0, 0, 0, 0)
17
+ self.tags: list[str] = []
18
+ self.parent_scene: bf.Scene | None = None
19
+ self.debug_color: tuple | str = "red"
20
+ self.render_order: int = 0
21
+ self.uid: int = Object.__count
22
+ Object.__used_uid.add(self.uid)
23
+
24
+ if Object.__available_uid:
25
+ self.name = Object.__available_uid.pop()
26
+ else:
27
+ self.name = Object.__count
28
+ Object.__count += 1
29
+
30
+ def __del__(self):
31
+ Object.__available_uid.add(self.uid)
32
+
33
+ def set_position(self, x, y) -> Self:
34
+ self.rect.topleft = x, y
35
+ return self
36
+
37
+ def set_center(self, x, y) -> Self:
38
+ self.rect.center = x, y
39
+ return self
40
+
41
+ def get_debug_outlines(self):
42
+ yield (self.rect, self.debug_color)
43
+
44
+ def set_debug_color(self, color) -> Self:
45
+ self.debug_color = color
46
+ return self
47
+
48
+ def set_parent_scene(self, scene) -> Self:
49
+ if scene == self.parent_scene:
50
+ return self
51
+ if self.parent_scene is not None:
52
+ self.do_when_removed()
53
+ self.parent_scene = scene
54
+ if scene is not None:
55
+ self.do_when_added()
56
+ return self
57
+
58
+ def do_when_added(self):
59
+ pass
60
+
61
+ def do_when_removed(self):
62
+ pass
63
+
64
+ def set_uid(self, uid: int) -> Self:
65
+ if uid in Object.__used_uid:
66
+ print(f"set_uid error : UID '{uid}' is already in use")
67
+ return self
68
+ self.uid = uid
69
+ Object.__used_uid.add(uid)
70
+ return self
71
+
72
+ def add_tags(self, *tags) -> Self:
73
+ for tag in tags:
74
+ if tag not in self.tags:
75
+ self.tags.append(tag)
76
+ self.tags.sort()
77
+ return self
78
+
79
+ def remove_tags(self, *tags):
80
+ self.tags = [tag for tag in self.tags if tag not in tags]
81
+
82
+ def has_tags(self, *tags) -> bool:
83
+ return all(tag in self.tags for tag in tags)
84
+
85
+ def get_tags(self) -> list[str]:
86
+ return self.tags
87
+
88
+ def process_event(self, event: pygame.Event) -> bool:
89
+ """
90
+ Returns bool : True if the method is blocking (no propagation to next object of the scene)
91
+ """
92
+ if event.consumed:
93
+ return
94
+ self.do_process_actions(event)
95
+ self.do_handle_event(event)
96
+
97
+ def do_process_actions(self, event: pygame.Event) -> None:
98
+ """
99
+ Process entity actions you may have set
100
+ """
101
+
102
+ def do_reset_actions(self) -> None:
103
+ """
104
+ Reset entity actions you may have set
105
+ """
106
+
107
+ def do_handle_event(self, event: pygame.Event):
108
+ """
109
+ Handle specific events with no action support
110
+ """
111
+ return False
112
+
113
+ def update(self, dt: float) -> None:
114
+ """
115
+ Update method to be overriden by subclasses of object (must call do_update and do_reset_actions)
116
+ """
117
+ self.do_update(dt)
118
+ self.do_reset_actions()
119
+
120
+ def do_update(self, dt: float) -> None:
121
+ """
122
+ Update method to be overriden for specific behavior by the end user
123
+ """
@@ -0,0 +1,115 @@
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.Entity):
82
+ def __init__(self) -> None:
83
+ super().__init__((0, 0))
84
+ self.particles: list[Particle] = []
85
+
86
+ def get_debug_outlines(self):
87
+ return
88
+ for particle in self.particles:
89
+ yield (
90
+ particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
91
+ "blue",
92
+ )
93
+ yield (self.rect, "cyan")
94
+
95
+ def add_particle(self, particle:Particle):
96
+ particle.generator = self
97
+ particle.do_when_added()
98
+ self.particles.append(particle)
99
+
100
+ def clear(self):
101
+ self.particles = []
102
+
103
+ def update(self, dt: float):
104
+ particles_to_remove = []
105
+ for particle in self.particles:
106
+ particle.update(dt)
107
+ if particle.dead:
108
+ particles_to_remove.append(particle)
109
+ for p in particles_to_remove:
110
+ self.particles.remove(p)
111
+
112
+ def draw(self, camera) -> None:
113
+ camera.surface.fblits(
114
+ [(p.surface, camera.world_to_screen(p.rect)) for p in self.particles]
115
+ )
@@ -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,100 @@
1
+ import batFramework as bf
2
+ import os
3
+ import pygame
4
+ import sys
5
+ import json
6
+ from typing import Any
7
+ from .utils import Singleton
8
+
9
+ if getattr(sys, "frozen", False):
10
+ # If the application is run as a bundle, the PyInstaller bootloader
11
+ # extends the sys module by a flag frozen=True and sets the app
12
+ # path into variable _MEIPASS'.
13
+ application_path = sys._MEIPASS
14
+ else:
15
+ application_path = os.getcwd()
16
+
17
+
18
+ class ResourceManager(metaclass=Singleton):
19
+ def __init__(self):
20
+ self.shared_variables: dict[str,Any] = {}
21
+ self.convert_image_cache = {}
22
+ self.convert_alpha_image_cache = {}
23
+ self.sound_cache = {}
24
+ self.RESOURCE_PATH = "."
25
+
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] == "."]
30
+
31
+ for file in files:
32
+ file_path = os.path.join(root, file)
33
+ if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif")):
34
+ self.load_image(file_path)
35
+ # print(f"Loaded image : '{file_path}'")
36
+
37
+ elif file.lower().endswith((".mp3", ".wav")):
38
+ bf.AudioManager().load_sound(file.split(".")[0], file_path)
39
+ # print(f"Loaded sound : '{file_path}'")
40
+
41
+ elif file.lower().endswith((".ttf", ".otf")):
42
+ bf.FontManager().load_font(file_path, file.split(".")[0])
43
+ # print(f"Loaded font : '{file_path}'")
44
+
45
+ print(f"Loaded resources in directory : '{path}'")
46
+
47
+ def set_resource_path(self, path: str):
48
+ self.RESOURCE_PATH = os.path.join(application_path, path)
49
+ print(f"Resource path : '{self.RESOURCE_PATH}'")
50
+
51
+ def get_path(self, path: str) -> str:
52
+ # Normalize path separators
53
+ normalized_path = path.replace("/", os.sep).replace("\\", os.sep)
54
+ return os.path.join(self.RESOURCE_PATH, normalized_path)
55
+
56
+ def load_image(self, path) -> None:
57
+ key = self.get_path(path)
58
+ if key in self.convert_image_cache:
59
+ return
60
+ self.convert_image_cache[key] = pygame.image.load(path).convert()
61
+ self.convert_alpha_image_cache[key] = pygame.image.load(path).convert_alpha()
62
+
63
+ def get_image(self, path, convert_alpha: bool = False) -> pygame.Surface | None:
64
+ key = self.get_path(path)
65
+ return (
66
+ self.convert_alpha_image_cache.get(key, None)
67
+ if convert_alpha
68
+ else self.convert_image_cache.get(key, None)
69
+ )
70
+
71
+ def load_json_from_file(self, path: str) -> dict | None:
72
+ try:
73
+ with open(self.get_path(path), "r") as file:
74
+ data = json.load(file)
75
+ return data
76
+ except FileNotFoundError:
77
+ print(f"File '{path}' not found")
78
+ return None
79
+
80
+ def save_json_to_file(self, path: str, data) -> bool:
81
+ try:
82
+ with open(self.get_path(path), "w") as file:
83
+ json.dump(data, file, indent=2)
84
+ return True
85
+ except FileNotFoundError:
86
+ return False
87
+
88
+
89
+ def set_sharedVar(self, name, value) -> bool:
90
+ """
91
+ Set a shared variable of any type. This will be accessible (read/write) from any scene
92
+ """
93
+ self.shared_variables[name] = value
94
+ return True
95
+
96
+ def get_sharedVar(self, name, error_value=None):
97
+ """
98
+ Get a shared variable
99
+ """
100
+ return self.shared_variables.get(name, error_value)