batframework 1.0.8a2__py3-none-any.whl → 1.0.8a4__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 +117 -73
  5. batFramework/audioManager.py +69 -26
  6. batFramework/camera.py +259 -69
  7. batFramework/constants.py +16 -54
  8. batFramework/cutscene.py +39 -29
  9. batFramework/cutsceneBlocks.py +36 -43
  10. batFramework/dynamicEntity.py +17 -9
  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 +221 -0
  18. batFramework/gui/constraints/__init__.py +1 -0
  19. batFramework/gui/constraints/constraints.py +730 -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 +292 -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 +134 -38
  32. batFramework/gui/shape.py +259 -57
  33. batFramework/gui/slider.py +230 -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 +103 -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 +100 -0
  44. batFramework/scene.py +281 -123
  45. batFramework/sceneManager.py +141 -108
  46. batFramework/scrollingSprite.py +114 -0
  47. batFramework/sprite.py +51 -0
  48. batFramework/stateMachine.py +2 -2
  49. batFramework/tileset.py +46 -0
  50. batFramework/time.py +123 -58
  51. batFramework/transition.py +195 -124
  52. batFramework/utils.py +87 -151
  53. batframework-1.0.8a4.dist-info/LICENCE +21 -0
  54. batframework-1.0.8a4.dist-info/METADATA +55 -0
  55. batframework-1.0.8a4.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.8a4.dist-info}/WHEEL +0 -0
  65. {batframework-1.0.8a2.dist-info → batframework-1.0.8a4.dist-info}/top_level.txt +0 -0
batFramework/scene.py CHANGED
@@ -1,203 +1,333 @@
1
1
  from __future__ import annotations
2
2
  import re
3
- from typing import TYPE_CHECKING,Any
3
+ from typing import TYPE_CHECKING, Any
4
+ from collections import OrderedDict
5
+ import itertools
6
+
4
7
  if TYPE_CHECKING:
5
8
  from .manager import Manager
9
+ from .sceneManager import SceneManager
6
10
  import pygame
7
11
  import batFramework as bf
8
12
 
9
13
 
10
14
  class Scene:
11
- def __init__(self, name, enable_alpha=True) -> None:
12
- self._name = name
13
- self._active = False
14
- self._visible = False
15
- self._world_entities: list[bf.Entity] = []
16
- self._hud_entities: list[bf.Entity] = []
15
+ def __init__(
16
+ self,
17
+ name: str,
18
+ hud_convert_alpha: bool = True,
19
+ world_convert_alpha: bool = False,
20
+ ) -> None:
21
+ """
22
+ Initialize the Scene object.
23
+
24
+ Args:
25
+ name: Name of the scene.
26
+ enable_alpha (bool, optional): Enable alpha channel for the scene surfaces. Defaults to True.
27
+ """
28
+ self.scene_index = 0
29
+ self.name = name
30
+ bf.TimeManager().add_register(self.name,False)
17
31
  self.manager: Manager | None = None
32
+ self.active = False
33
+ self.visible = False
34
+ self.world_entities: OrderedDict[bf.Entity, None] = OrderedDict()
35
+ self.hud_entities: OrderedDict[bf.Entity, None] = OrderedDict()
18
36
  self.actions: bf.ActionContainer = bf.ActionContainer()
19
- self.camera: bf.Camera = bf.Camera()
20
- self.scene_index = 0
21
- self.hud_camera: bf.Camera = bf.Camera()
22
- if enable_alpha:
23
- self.camera.surface = self.camera.surface.convert_alpha()
24
- self.hud_camera.surface = self.camera.surface.convert_alpha()
37
+ self.early_actions: bf.ActionContainer = bf.ActionContainer()
38
+ self.camera: bf.Camera = bf.Camera(convert_alpha=world_convert_alpha)
39
+ self.hud_camera: bf.Camera = bf.Camera(convert_alpha=hud_convert_alpha)
40
+ self.should_sort :bool = True
41
+ self.root: bf.Root = bf.Root(self.hud_camera)
42
+ self.root.rect.center = self.hud_camera.get_center()
43
+ self.add_hud_entity(self.root)
44
+ self.entities_to_remove = []
45
+ self.entities_to_add = []
25
46
 
26
- self.camera.set_clear_color((0, 0, 0))
27
- self.hud_camera.set_clear_color((0, 0, 0, 0))
28
47
 
29
- self.root : bf.Root = bf.Root()
30
- self.root.set_center(*self.hud_camera.get_center())
31
- self.add_hud_entity(self.root)
32
- self.blit_calls = 0
48
+ def __str__(self)->str:
49
+ return f"Scene({self.name})"
33
50
 
34
- def set_scene_index(self,index):
51
+ def get_world_entity_count(self) -> int:
52
+ return len(self.world_entities)
53
+
54
+ def get_hud_entity_count(self) -> int:
55
+ n = 0
56
+
57
+ def adder(e):
58
+ nonlocal n
59
+ n += len(e.children)
60
+
61
+ self.root.visit(adder)
62
+
63
+ return len(self.hud_entities) + n
64
+
65
+ def set_scene_index(self, index: int):
66
+ """Set the scene index."""
35
67
  self.scene_index = index
36
68
 
37
- def get_scene_index(self):
69
+ def get_scene_index(self) -> int:
70
+ """Get the scene index."""
38
71
  return self.scene_index
39
72
 
40
- def set_sharedVar(self, name, value)->bool:
41
- if not self.manager : return False
73
+ def set_sharedVar(self, name: str, value: Any) -> bool:
74
+ """
75
+ Set a shared variable in the manager.
76
+
77
+ Args:
78
+ name: Name of the shared variable.
79
+ value: Value to set.
80
+
81
+ Returns:
82
+ bool: True if setting was successful, False otherwise.
83
+ """
84
+ if not self.manager:
85
+ return False
42
86
  return self.manager.set_sharedVar(name, value)
43
87
 
44
- def get_sharedVar(self, name)->Any:
45
- if not self.manager : return False
46
- return self.manager.get_sharedVar(name)
88
+ def get_sharedVar(self, name: str, error_value=None) -> Any:
89
+ """
90
+ Get a shared variable from the manager.
91
+
92
+ Args:
93
+ name: Name of the shared variable.
94
+
95
+ Returns:
96
+ Any: Value of the shared variable.
97
+ """
98
+ if not self.manager:
99
+ return error_value
100
+ return self.manager.get_sharedVar(name, error_value)
47
101
 
48
102
  def do_when_added(self):
49
103
  pass
50
104
 
51
- def set_clear_color(self, color: pygame.Color|tuple):
105
+ def set_clear_color(self, color: pygame.Color | tuple):
106
+ """Set the clear color for the camera."""
52
107
  self.camera.set_clear_color(color)
53
- # self.hud_camera.set_clear_color(color)
54
108
 
55
109
  def set_manager(self, manager_link: Manager):
110
+ """Set the manager link for the scene."""
56
111
  self.manager = manager_link
112
+ self.manager.update_scene_states()
57
113
 
58
114
  def set_visible(self, value: bool):
59
- self._visible = value
60
- if self.manager : self.manager.update_scene_states()
115
+ """Set the visibility of the scene."""
116
+ self.visible = value
117
+ if self.manager:
118
+ self.manager.update_scene_states()
61
119
 
62
120
  def set_active(self, value):
63
- self._active = value
64
- if self.manager : self.manager.update_scene_states()
121
+ """Set the activity of the scene."""
122
+ self.active = value
123
+ if self.manager:
124
+ self.manager.update_scene_states()
65
125
 
66
126
  def is_active(self) -> bool:
67
- return self._active
127
+ """Check if the scene is active."""
128
+ return self.active
68
129
 
69
130
  def is_visible(self) -> bool:
70
- return self._visible
131
+ """Check if the scene is visible."""
132
+ return self.visible
71
133
 
72
134
  def get_name(self) -> str:
73
- return self._name
74
-
75
- def add_world_entity(self, *entity: bf.Entity):
76
- for e in entity:
77
- if e not in self._world_entities:
78
-
79
- self._world_entities.append(e)
80
- e.parent_scene = self
81
- e.do_when_added()
82
-
83
- def remove_world_entity(self, *entity: bf.Entity):
84
- for e in entity:
85
- if e not in self._world_entities:
86
- return False
87
- e.do_when_removed()
88
- e.parent_scene = None
89
- self._world_entities.remove(e)
90
- return True
91
-
92
- def add_hud_entity(self, *entity: bf.Entity):
93
- for e in entity:
94
- if e not in self._hud_entities:
95
- self._hud_entities.append(e)
96
- e.parent_scene = self
97
- e.do_when_added()
98
-
99
- def remove_hud_entity(self, *entity: bf.Entity):
100
- for e in entity:
101
- if e in self._hud_entities:
102
- e.do_when_removed()
103
- e.parent_scene = None
104
- self._hud_entities.remove(e)
105
-
106
- def add_action(self, *action):
107
- self.actions.add_action(*action)
135
+ """Get the name of the scene."""
136
+ return self.name
137
+
138
+ def add_world_entity(self, *entities: bf.Entity):
139
+ """Add world entities to the scene."""
140
+ change = False
141
+ for e in entities:
142
+ if e not in self.world_entities and e not in self.entities_to_add:
143
+ change = True
144
+ # self.world_entities[e] = None
145
+ self.entities_to_add.append(e)
146
+ e.set_parent_scene(self)
147
+ # self.sort_entities()
148
+ return change
149
+
150
+ # Updated remove_world_entity method to add entities to the removal list
151
+ def remove_world_entity(self, *entities: bf.Entity):
152
+ """Mark world entities for removal from the scene."""
153
+ change = False
154
+ for e in entities:
155
+ if e in self.world_entities:
156
+ change = True
157
+ self.entities_to_remove.append(e)
158
+ e.set_parent_scene(None)
159
+ return change
160
+
161
+ def add_hud_entity(self, *entities: bf.Entity):
162
+ """Add HUD entities to the scene."""
163
+ for e in entities:
164
+ if e not in self.hud_entities:
165
+ self.hud_entities[e] = None
166
+ e.set_parent_scene(self)
167
+ self.sort_entities()
168
+ return True
169
+
170
+ def remove_hud_entity(self, *entities: bf.Entity):
171
+ """Remove HUD entities from the scene."""
172
+ for e in entities:
173
+ if e in self.hud_entities:
174
+ e.set_parent_scene(None)
175
+ self.hud_entities.pop(e)
176
+
177
+ def add_actions(self, *action):
178
+ """Add actions to the scene."""
179
+ self.actions.add_actions(*action)
180
+
181
+ def add_early_actions(self, *action):
182
+ """Add actions to the scene."""
183
+ self.early_actions.add_actions(*action)
108
184
 
109
185
  def get_by_tags(self, *tags):
110
- return [
186
+ """Get entities by their tags."""
187
+ res = [
111
188
  entity
112
- for entity in self._world_entities + self._hud_entities
113
- if any(entity.has_tag(t) for t in tags)
189
+ for entity in itertools.chain(
190
+ self.world_entities.keys(), self.hud_entities.keys()
191
+ )
192
+ if any(entity.has_tags(t) for t in tags)
114
193
  ]
194
+ res.extend(list(self.root.get_by_tags(*tags)))
195
+ return res
115
196
 
116
197
  def get_by_uid(self, uid) -> bf.Entity | None:
117
- return next(
118
- (
119
- entity
120
- for entity in self._world_entities + self._hud_entities
121
- if entity.uid == uid
122
- ),
123
- None,
198
+ """Get an entity by its unique identifier."""
199
+ res = self._find_entity_by_uid(
200
+ uid, itertools.chain(self.world_entities.keys(), self.hud_entities.keys())
124
201
  )
202
+ if res is None:
203
+ res = self._recursive_search_by_uid(uid, self.root)
204
+ return res
125
205
 
126
- # called before process event
127
- def do_early_process_event(self, event: pygame.Event) -> bool:
128
- """return True if stop event propagation in child entities and scene's action container"""
129
- return False
206
+ def _find_entity_by_uid(self, uid, entities) -> bf.Entity | None:
207
+ """Search for entity by uid in a list of entities."""
208
+ for entity in entities:
209
+ if entity.uid == uid:
210
+ return entity
211
+ return None
212
+
213
+ def _recursive_search_by_uid(self, uid, widget) -> bf.Entity | None:
214
+ """Recursively search for entity by uid in the widget's children."""
215
+ if widget.uid == uid:
216
+ return widget
217
+
218
+ for child in widget.children:
219
+ res = self._recursive_search_by_uid(uid, child)
220
+ if res is not None:
221
+ return res
222
+
223
+ return None
130
224
 
131
- # propagates event to all entities
132
225
  def process_event(self, event: pygame.Event):
133
226
  """
134
- Propagates event to child events. Calls early process event first, if returns False then stops. Processes scene's action_container, then custom do_handle_event function.
135
- Finally resets the action_container, and propagates to all child entities. if any of them returns True, the propagation is stopped.
227
+ Propagates event while it is not consumed.
228
+ In order :
229
+ -do_early_handle_event()
230
+ -scene early_actions
231
+ -propagate to scene entities (hud then world)
232
+ -do_handle_event()
233
+ -scene actions
234
+ at each step, if the event is consumed the propagation stops
136
235
  """
137
- if self.get_sharedVar("in_transition"):
236
+ if event.consumed:
138
237
  return
139
- if self.do_early_process_event(event):
238
+ self.do_early_handle_event(event)
239
+ if event.consumed:
140
240
  return
141
- self.actions.process_event(event)
241
+ self.early_actions.process_event(event)
242
+ if event.consumed:
243
+ return
244
+ for entity in itertools.chain(
245
+ self.hud_entities.keys(), self.world_entities.keys()
246
+ ):
247
+ entity.process_event(event)
248
+ if event.consumed:
249
+ return
142
250
  self.do_handle_event(event)
143
- for entity in self._world_entities + self._hud_entities:
144
- if entity.process_event(event):
145
- break
146
- self.actions.reset()
251
+ if event.consumed:
252
+ return
253
+ self.actions.process_event(event)
254
+
255
+ # called before process event
256
+ def do_early_handle_event(self, event: pygame.Event):
257
+ """Called early in event propagation"""
258
+ pass
147
259
 
148
260
  def do_handle_event(self, event: pygame.Event):
149
261
  """called inside process_event but before resetting the scene's action container and propagating event to child entities of the scene"""
150
262
  pass
151
263
 
152
264
  def update(self, dt):
153
- for entity in self._world_entities + self._hud_entities:
265
+ """Update the scene. Do NOT override"""
266
+ if self.should_sort:
267
+ self._sort_entities_internal()
268
+
269
+ for entity in itertools.chain(
270
+ self.hud_entities.keys(), self.world_entities.keys()
271
+ ):
154
272
  entity.update(dt)
273
+
155
274
  self.do_update(dt)
156
275
  self.camera.update(dt)
157
276
  self.hud_camera.update(dt)
277
+ self.actions.reset()
278
+ self.early_actions.reset()
279
+
280
+
281
+ if self.entities_to_add:
282
+ for e in self.entities_to_add:
283
+ self.world_entities[e] = None
284
+ self.entities_to_add.clear()
285
+
286
+ # Remove marked entities after updating
287
+ if self.entities_to_remove:
288
+ for e in self.entities_to_remove:
289
+ self.world_entities.pop(e, None)
290
+ self.entities_to_remove.clear()
158
291
 
159
292
  def do_update(self, dt):
293
+ """Specific update within the scene."""
160
294
  pass
161
295
 
162
296
  def debug_entity(self, entity: bf.Entity, camera: bf.Camera):
163
- # return
164
- if not entity.visible:
165
- return
166
- for data in entity.get_bounding_box():
167
- if isinstance(data,pygame.FRect):
297
+ def draw_rect(data):
298
+ if data is None:
299
+ return
300
+ if isinstance(data, pygame.FRect) or isinstance(data, pygame.Rect):
168
301
  rect = data
169
- color = entity._debug_color
302
+ color = entity.debug_color
170
303
  else:
171
304
  rect = data[0]
172
305
  color = data[1]
173
- if not isinstance(color,pygame.Color): color = pygame.Color(color)
306
+ pygame.draw.rect(camera.surface, color, camera.world_to_screen(rect), 1)
174
307
 
175
- pygame.draw.rect(camera.surface, color , camera.transpose(rect), 1)
176
-
177
- def draw(self, surface: pygame.Surface):
178
- self._world_entities.sort(key=lambda e: (e.z_depth,e.render_order))
179
- self._hud_entities.sort(key=lambda e: (e.z_depth,e.render_order))
180
-
181
- total_blit_calls = 0
182
- self.camera.clear()
183
- self.hud_camera.clear()
308
+ [draw_rect(data) for data in entity.get_debug_outlines()]
184
309
 
310
+ def sort_entities(self) -> None:
311
+ self.should_sort = True
185
312
 
186
- total_blit_calls += sum(
187
- entity.draw(self.camera) for entity in self._world_entities
313
+ def _sort_entities_internal(self):
314
+ """Sort entities within the scene based on their rendering order."""
315
+ self.world_entities = OrderedDict(
316
+ sorted(self.world_entities.items(), key=lambda e: e[0].render_order)
188
317
  )
189
-
190
- if self.manager and self.manager._debugging == 2:
191
- for entity in self._world_entities:
192
- self.debug_entity(entity, self.camera)
193
-
194
- total_blit_calls += sum(
195
- entity.draw(self.hud_camera) for entity in self._hud_entities
318
+ self.hud_entities = OrderedDict(
319
+ sorted(self.hud_entities.items(), key=lambda e: e[0].render_order)
196
320
  )
197
- if self.manager and self.manager._debugging == 2:
198
- for entity in self._hud_entities:
199
- self.debug_entity(entity, self.hud_camera)
321
+ self.should_sort = False
200
322
 
323
+ def draw(self, surface: pygame.Surface):
324
+ self.camera.clear()
325
+ self.hud_camera.clear()
326
+
327
+ # Draw all world entities
328
+ self._draw_camera(self.camera, self.world_entities.keys())
329
+ # Draw all HUD entities
330
+ self._draw_camera(self.hud_camera, self.hud_entities.keys())
201
331
 
202
332
  self.do_early_draw(surface)
203
333
  self.camera.draw(surface)
@@ -205,7 +335,12 @@ class Scene:
205
335
  self.hud_camera.draw(surface)
206
336
  self.do_final_draw(surface)
207
337
 
208
- self.blit_calls = total_blit_calls
338
+ def _draw_camera(self, camera: bf.Camera, entity_list):
339
+ _ = [entity.draw(camera) for entity in entity_list]
340
+ debugMode = self.manager.debug_mode
341
+ # Draw outlines for world entities if required
342
+ if debugMode == bf.debugMode.OUTLINES:
343
+ [self.debug_entity(e, camera) for e in entity_list]
209
344
 
210
345
  def do_early_draw(self, surface: pygame.Surface):
211
346
  pass
@@ -219,8 +354,31 @@ class Scene:
219
354
  def on_enter(self):
220
355
  self.set_active(True)
221
356
  self.set_visible(True)
357
+ self.root.clear_hovered()
358
+ # self.root.clear_focused()
359
+ self.root.build()
360
+ bf.TimeManager().activate_register(self.name)
361
+ self.do_on_enter()
362
+ # self.root.visit(lambda e : e.resolve_constraints())
222
363
 
223
364
  def on_exit(self):
365
+ self.root.clear_hovered()
366
+ # self.root.clear_focused()
224
367
  self.set_active(False)
225
368
  self.set_visible(False)
226
369
  self.actions.hard_reset()
370
+ self.early_actions.hard_reset()
371
+ bf.TimeManager().deactivate_register(self.name)
372
+ self.do_on_exit()
373
+
374
+ def do_on_enter(self) -> None:
375
+ pass
376
+
377
+ def do_on_exit(self) -> None:
378
+ pass
379
+
380
+ def do_on_enter_early(self) -> None:
381
+ pass
382
+
383
+ def do_on_exit_early(self) -> None:
384
+ pass