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
@@ -0,0 +1,158 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from .stateMachine import *
4
+
5
+
6
+
7
+ class PlatformController(bf.DynamicEntity):
8
+ def __init__(self,*args,**kwargs):
9
+ super().__init__(*args,**kwargs)
10
+ self.control = bf.ActionContainer()
11
+ self.speed = 500
12
+ self.acceleration = 100
13
+ self.jump_force = -500
14
+ self.gravity = 1200
15
+ self.terminal_velocity = 1000
16
+ self.on_ground = False
17
+ self.friction = 0.7
18
+
19
+ def reset_actions(self):
20
+ self.control.reset()
21
+
22
+ def process_actions(self, event):
23
+ self.control.process_event(event)
24
+
25
+ def check_collision_y(self):
26
+ pass
27
+
28
+ def check_collision_x(self):
29
+ pass
30
+
31
+ def update(self, dt):
32
+ super().update(dt)
33
+ if (not self.control.is_active("right")) and (not self.control.is_active("left")):
34
+ self.velocity.x *= self.friction
35
+ if abs(self.velocity.x) <= 0.01:
36
+ self.velocity.x = 0
37
+ if not self.on_ground:
38
+ self.velocity.y += self.gravity * dt
39
+ if self.velocity.y > self.terminal_velocity:
40
+ self.velocity.y = self.terminal_velocity
41
+ if self.control.is_active("left"):
42
+ self.velocity.x -= self.acceleration
43
+ if self.control.is_active("right"):
44
+ self.velocity.x += self.acceleration
45
+ if self.on_ground and self.control.is_active("jump") :
46
+ self.velocity.y = self.jump_force
47
+ self.on_ground = False
48
+
49
+ self.velocity.x = pygame.math.clamp(self.velocity.x,-self.speed,self.speed)
50
+ self.rect.y += self.velocity.y * dt
51
+ self.check_collision_y()
52
+ self.rect.x += self.velocity.x * dt
53
+ self.check_collision_x()
54
+
55
+
56
+
57
+ class TopDownController(bf.DynamicEntity):
58
+ def __init__(self,*args,**kwargs):
59
+ super().__init__(*args,**kwargs)
60
+ self.control = bf.ActionContainer()
61
+ self.input_velocity = pygame.Vector2()
62
+ self.speed = 500
63
+ self.acceleration = 100
64
+ self.friction = 0
65
+
66
+ def reset_actions(self):
67
+ self.control.reset()
68
+
69
+ def process_actions(self, event):
70
+ self.control.process_event(event)
71
+
72
+ def check_collision_y(self):
73
+ pass
74
+
75
+ def check_collision_x(self):
76
+ pass
77
+
78
+ def update(self, dt):
79
+ super().update(dt)
80
+ self.input_velocity.update(0,0)
81
+ self.velocity *= self.friction
82
+
83
+ if abs(self.velocity.x) <= 0.01:
84
+ self.velocity.x = 0
85
+ if self.control.is_active("left"):
86
+ self.input_velocity[0] = -self.acceleration
87
+ if self.control.is_active("right"):
88
+ self.input_velocity[0] = self.acceleration
89
+ if self.control.is_active("up"):
90
+ self.input_velocity[1] = -self.acceleration
91
+ if self.control.is_active("down"):
92
+ self.input_velocity[1] = self.acceleration
93
+
94
+ if self.input_velocity:
95
+ self.input_velocity.normalize_ip()
96
+
97
+ self.velocity += self.input_velocity * self.acceleration
98
+ self.velocity.clamp_magnitude_ip(self.speed)
99
+
100
+
101
+ self.rect.x += self.velocity.x * dt
102
+ self.check_collision_x()
103
+ self.rect.y += self.velocity.y * dt
104
+ self.check_collision_y()
105
+
106
+
107
+
108
+
109
+ class CameraController(bf.Entity):
110
+ def __init__(self, *args, **kwargs):
111
+ super().__init__(*args, **kwargs)
112
+ self.origin = None # Previous frame's world mouse pos
113
+ self.mouse_actions = bf.ActionContainer(
114
+ bf.Action("control").add_key_control(pygame.K_LCTRL).set_holding(),
115
+ bf.Action("drag").add_mouse_control(1).set_holding().set_consume_event(True),
116
+ bf.Action("zoom_in").add_mouse_control(4).set_consume_event(True),
117
+ bf.Action("zoom_out").add_mouse_control(5).set_consume_event(True),
118
+ )
119
+
120
+ def reset_actions(self):
121
+ self.mouse_actions.reset()
122
+
123
+ def process_actions(self, event):
124
+ self.mouse_actions.process_event(event)
125
+ if self.mouse_actions["drag"] and event.type == pygame.MOUSEMOTION:
126
+ event.consumed = True
127
+
128
+ def do_update(self, dt):
129
+ cam = self.parent_layer.camera
130
+ if self.mouse_actions["zoom_in"]:
131
+ if self.mouse_actions["control"]:
132
+ cam.rotate_by(10)
133
+ else:
134
+ cam.zoom(cam.zoom_factor * 1.1)
135
+
136
+ elif self.mouse_actions["zoom_out"]:
137
+ if self.mouse_actions["control"]:
138
+ cam.rotate_by(-10)
139
+ else:
140
+ cam.zoom(cam.zoom_factor / 1.1)
141
+
142
+ if self.mouse_actions["drag"]:
143
+ mouse_world = cam.get_mouse_pos()
144
+ cam_pos = cam.world_rect.topleft
145
+
146
+ if self.origin is None:
147
+ self.origin = (mouse_world[0] - cam_pos[0], mouse_world[1] - cam_pos[1])
148
+ else:
149
+ offset = (mouse_world[0] - cam_pos[0], mouse_world[1] - cam_pos[1])
150
+ dx = offset[0] - self.origin[0]
151
+ dy = offset[1] - self.origin[1]
152
+
153
+ cam.move_by(-dx, -dy)
154
+ self.origin = (mouse_world[0] - cam_pos[0], mouse_world[1] - cam_pos[1])
155
+
156
+
157
+ else:
158
+ self.origin = None
@@ -0,0 +1,39 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from batFramework.stateMachine import State,StateMachine
4
+ from ..animation import Animation
5
+ from ..animatedSprite import AnimatedSprite
6
+
7
+ class AnimatedState(State):
8
+
9
+ def __init__(self, animation:str):
10
+ super().__init__(animation)
11
+ self._transition : dict[str,str] = {}
12
+ self.animation = animation
13
+
14
+ def add_transition(self,dest:str,transition_animation:str):
15
+ self._transition[dest] = transition_animation
16
+
17
+
18
+ class AnimatedStateMachine(StateMachine):
19
+ def __init__(self, parent:AnimatedSprite):
20
+ self.states: dict[str, AnimatedState] = {}
21
+ self.parent:AnimatedSprite = parent
22
+ self.current_state = None
23
+
24
+ def set_state(self, state_name: str,reset_counter:bool = True):
25
+
26
+ if state_name not in self.states:
27
+ return
28
+
29
+ if self.current_state:
30
+ self.current_state.on_exit()
31
+
32
+ if self.current_state and state_name in self.current_state._transition:
33
+ self.parent.set_animation(self.current_state._transition[state_name],reset_counter,0,self.states[state_name].animation)
34
+ else:
35
+ self.parent.set_animation(self.states[state_name].animation,reset_counter)
36
+
37
+ self.current_state = self.states[state_name]
38
+ self.current_state.on_enter()
39
+
@@ -0,0 +1,46 @@
1
+ import pygame
2
+ from .resourceManager import ResourceManager
3
+ import batFramework as bf
4
+
5
+
6
+ class Tileset:
7
+ def __init__(self, source: pygame.Surface, tilesize: tuple[int, int]) -> None:
8
+ self.surface = source
9
+ self.tile_size = tilesize
10
+ self.tile_width = source.get_width() // tilesize[0]
11
+ self.tile_height = source.get_height() // tilesize[1]
12
+ # Create flipped versions of the tileset surface
13
+ self.flipped_surfaces = {
14
+ (False, False): self.surface,
15
+ (False, True): pygame.transform.flip(self.surface, False, True),
16
+ (True, False): pygame.transform.flip(self.surface, True, False),
17
+ (True, True): pygame.transform.flip(self.surface, True, True),
18
+ }
19
+
20
+ # Split each of the surfaces into tiles
21
+ self.tile_dict = {}
22
+ for flip_state, surf in self.flipped_surfaces.items():
23
+ tiles = bf.utils.split_surface(surf, self.tile_size)
24
+ for coord, tile in tiles.items():
25
+ if coord not in self.tile_dict:
26
+ self.tile_dict[coord] = {}
27
+ self.tile_dict[coord][flip_state] = tile
28
+
29
+ def __str__(self) -> str:
30
+ num_tiles = 0
31
+ if self.tile_dict:
32
+ num_tiles = len(self.tile_dict.values())
33
+ return f"{num_tiles} tiles | Tile size : {self.tile_size}"
34
+
35
+ def get_tile(
36
+ self, x: int, y: int, flipX=False, flipY=False
37
+ ) -> pygame.Surface | None:
38
+ if flipX:
39
+ x = self.tile_width - 1 - x
40
+ if flipY:
41
+ y = self.tile_height - 1 - y
42
+ tile_data = self.tile_dict.get((x, y), None)
43
+ if tile_data is None:
44
+ return None
45
+
46
+ return tile_data.get((flipX, flipY), None)
@@ -0,0 +1,213 @@
1
+ import batFramework as bf
2
+ from typing import Callable, Union, Self,Any
3
+
4
+ class Timer:
5
+ _count: int = 0
6
+ _available_ids: set[int] = set()
7
+
8
+ def __init__(self, duration: float, end_callback: Callable[[], Any], loop: int = 0, register: str = "global") -> None:
9
+ if Timer._available_ids:
10
+ self.uid = Timer._available_ids.pop()
11
+ else:
12
+ self.uid = Timer._count
13
+ Timer._count += 1
14
+
15
+ self.register = register
16
+ self.duration: float = duration
17
+ self.end_callback = end_callback
18
+
19
+ self.elapsed_time: float = 0
20
+ self.is_over: bool = False
21
+ self.loop: int = loop # Number of loops (-1 for infinite)
22
+ self.is_paused: bool = False
23
+ self.do_delete: bool = False
24
+ self.is_stopped: bool = True
25
+
26
+ def __bool__(self) -> bool:
27
+ return self.elapsed_time != -1 and self.is_over
28
+
29
+ def __str__(self) -> str:
30
+ loop_info = "infinite" if self.loop == -1 else f"{self.loop} loops left"
31
+ return f"Timer ({self.uid}) {self.elapsed_time}/{self.duration} | {loop_info} {'(D) ' if self.do_delete else ''}"
32
+
33
+ def stop(self) -> Self:
34
+ """
35
+ Cancels all progression and stops the timer.
36
+ Does not mark it for deletion and does not call the end_callback.
37
+ Prevents automatic restart if looping.
38
+ """
39
+ self.is_stopped = True
40
+ self.is_paused = False
41
+ self.is_over = False
42
+ self.elapsed_time = 0
43
+ return self
44
+
45
+ def start(self, force: bool = False) -> Self:
46
+ """
47
+ Starts the timer only if not already started (unless force is used, which resets it).
48
+ """
49
+ if self.elapsed_time > 0 and not force:
50
+ return self
51
+ if not bf.TimeManager().add_timer(self, self.register):
52
+ return self
53
+ self.elapsed_time = 0
54
+ self.is_paused = False
55
+ self.is_over = False
56
+ self.is_stopped = False
57
+ return self
58
+
59
+ def pause(self) -> Self:
60
+ """
61
+ Momentarily stops the timer until resume is called.
62
+ """
63
+ self.is_paused = True
64
+ return self
65
+
66
+ def resume(self) -> Self:
67
+ """
68
+ Resumes from a paused state.
69
+ """
70
+ self.is_paused = False
71
+ return self
72
+
73
+ def delete(self) -> Self:
74
+ """
75
+ Marks the timer for deletion.
76
+ """
77
+ self.do_delete = True
78
+ return self
79
+
80
+ def has_started(self) -> bool:
81
+ """
82
+ Returns True if the timer has started.
83
+ """
84
+ return not self.is_stopped
85
+
86
+ def get_progression(self) -> float:
87
+ """
88
+ Returns the progression of the timer (0 to 1) as a float.
89
+ """
90
+ if self.is_stopped:
91
+ return 0
92
+ if self.elapsed_time >= self.duration:
93
+ return 1
94
+ return self.elapsed_time / self.duration
95
+
96
+ def update(self, dt) -> None:
97
+ if self.is_stopped or self.is_paused or self.is_over:
98
+ return
99
+ self.elapsed_time += dt
100
+ if self.get_progression() == 1:
101
+ self.end()
102
+
103
+ def end(self):
104
+ """
105
+ Ends the timer progression (calls the end_callback function).
106
+ Is called automatically once the timer is over.
107
+ Will not mark the timer for deletion.
108
+ If it is looping, it will restart the timer **only if it wasn't stopped**.
109
+ """
110
+ self.is_over = True
111
+ if self.end_callback:
112
+ self.end_callback()
113
+
114
+ # Handle looping
115
+ if self.loop == -1: # Infinite looping
116
+ self.elapsed_time = 0
117
+ self.start()
118
+ return
119
+ elif self.loop > 0: # Decrease loop count and restart
120
+ self.loop -= 1
121
+ self.elapsed_time = 0
122
+ self.start()
123
+ return
124
+
125
+ # Stop the timer if no loops are left
126
+ self.is_stopped = True
127
+
128
+ def should_delete(self) -> bool:
129
+ """
130
+ Method that returns if the timer is to be deleted.
131
+ Required for timer management.
132
+ """
133
+ return self.is_over or self.do_delete
134
+
135
+ def _release_id(self):
136
+ Timer._available_ids.add(self.uid)
137
+
138
+ class SceneTimer(Timer):
139
+ """
140
+ A timer that is only updated while the given scene is active (being updated)
141
+ """
142
+ def __init__(self, duration: float | int, end_callback, loop: int = 0, scene_name:str = "global") -> None:
143
+ super().__init__(duration, end_callback, loop, scene_name)
144
+
145
+ class TimeManager(metaclass=bf.Singleton):
146
+ class TimerRegister:
147
+ def __init__(self, active=True):
148
+ self.active = active
149
+ self.timers: dict[int | str, Timer] = {}
150
+
151
+ def __iter__(self):
152
+ return iter(self.timers.values())
153
+
154
+ def add_timer(self, timer: Timer):
155
+ self.timers[timer.uid] = timer
156
+
157
+ def update(self, dt):
158
+ expired_timers = []
159
+ for timer in list(self.timers.values()):
160
+ if not timer.is_paused:
161
+ timer.update(dt)
162
+ if timer.should_delete():
163
+ expired_timers.append(timer.uid)
164
+ for uid in expired_timers:
165
+ self.timers[uid]._release_id()
166
+ del self.timers[uid]
167
+
168
+ def __init__(self):
169
+ self.registers = {"global": TimeManager.TimerRegister()}
170
+
171
+ def add_register(self, name, active=True):
172
+ if name not in self.registers:
173
+ self.registers[name] = TimeManager.TimerRegister(active)
174
+
175
+ def remove_register(self, name):
176
+ if name not in self.registers:
177
+ return
178
+
179
+ self.registers.pop(name)
180
+
181
+
182
+ def add_timer(self, timer, register="global") -> bool:
183
+ if register in self.registers:
184
+ self.registers[register].add_timer(timer)
185
+ return True
186
+ print(f"Register '{register}' does not exist.")
187
+ return False
188
+
189
+ def get_active_registers(self) -> list[TimerRegister]:
190
+ return [t for t in self.registers.values() if t.active]
191
+
192
+ def update(self, dt):
193
+ for register in self.registers.values():
194
+ if register.active:
195
+ register.update(dt)
196
+
197
+ def activate_register(self, name, active=True):
198
+ if name in self.registers:
199
+ self.registers[name].active = active
200
+ else:
201
+ print(f"Register '{name}' does not exist.")
202
+
203
+ def deactivate_register(self, name):
204
+ self.activate_register(name, active=False)
205
+
206
+ def __str__(self)->str:
207
+ res = ""
208
+ for name,reg in self.registers.items():
209
+ if not reg.timers:continue
210
+ res +=name+"\n"
211
+ for t in reg.timers.values():
212
+ res +="\t"+str(t)+"\n"
213
+ return res