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.
- batFramework/__init__.py +53 -50
- batFramework/action.py +126 -99
- batFramework/actionContainer.py +53 -9
- batFramework/animatedSprite.py +115 -65
- batFramework/audioManager.py +69 -26
- batFramework/camera.py +259 -69
- batFramework/constants.py +16 -54
- batFramework/cutscene.py +36 -29
- batFramework/cutsceneBlocks.py +37 -42
- batFramework/dynamicEntity.py +9 -7
- batFramework/easingController.py +58 -0
- batFramework/entity.py +48 -97
- batFramework/enums.py +113 -0
- batFramework/fontManager.py +65 -0
- batFramework/gui/__init__.py +10 -2
- batFramework/gui/button.py +9 -78
- batFramework/gui/clickableWidget.py +219 -0
- batFramework/gui/constraints/__init__.py +1 -0
- batFramework/gui/constraints/constraints.py +590 -0
- batFramework/gui/container.py +174 -32
- batFramework/gui/debugger.py +131 -43
- batFramework/gui/dialogueBox.py +99 -0
- batFramework/gui/draggableWidget.py +40 -0
- batFramework/gui/image.py +54 -18
- batFramework/gui/indicator.py +38 -21
- batFramework/gui/interactiveWidget.py +177 -13
- batFramework/gui/label.py +288 -74
- batFramework/gui/layout.py +219 -60
- batFramework/gui/meter.py +71 -0
- batFramework/gui/radioButton.py +84 -0
- batFramework/gui/root.py +128 -38
- batFramework/gui/shape.py +253 -57
- batFramework/gui/slider.py +246 -0
- batFramework/gui/style.py +10 -0
- batFramework/gui/styleManager.py +48 -0
- batFramework/gui/textInput.py +137 -0
- batFramework/gui/toggle.py +115 -51
- batFramework/gui/widget.py +329 -254
- batFramework/manager.py +40 -19
- batFramework/object.py +114 -0
- batFramework/particle.py +101 -0
- batFramework/renderGroup.py +67 -0
- batFramework/resourceManager.py +84 -0
- batFramework/scene.py +242 -114
- batFramework/sceneManager.py +145 -107
- batFramework/scrollingSprite.py +115 -0
- batFramework/sprite.py +51 -0
- batFramework/stateMachine.py +2 -2
- batFramework/tileset.py +46 -0
- batFramework/time.py +117 -57
- batFramework/transition.py +184 -126
- batFramework/utils.py +31 -156
- batframework-1.0.8a3.dist-info/LICENCE +21 -0
- batframework-1.0.8a3.dist-info/METADATA +55 -0
- batframework-1.0.8a3.dist-info/RECORD +58 -0
- batFramework/debugger.py +0 -48
- batFramework/easing.py +0 -71
- batFramework/gui/constraints.py +0 -204
- batFramework/gui/frame.py +0 -19
- batFramework/particles.py +0 -77
- batFramework/transitionManager.py +0 -0
- batframework-1.0.8a2.dist-info/METADATA +0 -58
- batframework-1.0.8a2.dist-info/RECORD +0 -42
- {batframework-1.0.8a2.dist-info → batframework-1.0.8a3.dist-info}/WHEEL +0 -0
- {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
|
-
|
9
|
-
self.
|
10
|
-
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
|
-
|
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.
|
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
|
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
|
-
|
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
|
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
|
+
"""
|
batFramework/particle.py
ADDED
@@ -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
|