batframework 1.0.8a4__py3-none-any.whl → 1.0.8a6__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 (38) hide show
  1. batFramework/__init__.py +15 -1
  2. batFramework/animatedSprite.py +65 -50
  3. batFramework/character.py +27 -0
  4. batFramework/dynamicEntity.py +1 -0
  5. batFramework/enums.py +2 -2
  6. batFramework/fontManager.py +2 -2
  7. batFramework/gui/clickableWidget.py +6 -7
  8. batFramework/gui/constraints/constraints.py +125 -40
  9. batFramework/gui/image.py +14 -14
  10. batFramework/gui/interactiveWidget.py +15 -0
  11. batFramework/gui/label.py +44 -27
  12. batFramework/gui/layout.py +23 -14
  13. batFramework/gui/meter.py +10 -7
  14. batFramework/gui/radioButton.py +1 -1
  15. batFramework/gui/shape.py +3 -25
  16. batFramework/gui/slider.py +40 -30
  17. batFramework/gui/textInput.py +160 -50
  18. batFramework/gui/toggle.py +20 -22
  19. batFramework/gui/widget.py +65 -32
  20. batFramework/manager.py +17 -5
  21. batFramework/object.py +17 -8
  22. batFramework/particle.py +18 -4
  23. batFramework/scene.py +1 -1
  24. batFramework/sceneManager.py +42 -13
  25. batFramework/stateMachine.py +9 -6
  26. batFramework/templates/__init__.py +2 -0
  27. batFramework/templates/character.py +44 -0
  28. batFramework/templates/states.py +166 -0
  29. batFramework/time.py +30 -9
  30. batFramework/transition.py +2 -2
  31. batFramework/triggerZone.py +1 -1
  32. batFramework/utils.py +35 -6
  33. {batframework-1.0.8a4.dist-info → batframework-1.0.8a6.dist-info}/METADATA +3 -15
  34. batframework-1.0.8a6.dist-info/RECORD +62 -0
  35. {batframework-1.0.8a4.dist-info → batframework-1.0.8a6.dist-info}/WHEEL +1 -1
  36. batframework-1.0.8a4.dist-info/RECORD +0 -58
  37. {batframework-1.0.8a4.dist-info → batframework-1.0.8a6.dist-info}/LICENCE +0 -0
  38. {batframework-1.0.8a4.dist-info → batframework-1.0.8a6.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ import pygame
6
6
 
7
7
 
8
8
  class Toggle(Button):
9
- def __init__(self, text: str, callback=None, default_value: bool = False) -> None:
9
+ def __init__(self, text: str = "", callback=None, default_value: bool = False) -> None:
10
10
  self.value: bool = default_value
11
11
  self.indicator: ToggleIndicator = ToggleIndicator(default_value)
12
12
  self.gap: float | int = 0
@@ -55,37 +55,31 @@ class Toggle(Button):
55
55
  def get_min_required_size(self) -> tuple[float, float]:
56
56
  if not self.text_rect:
57
57
  self.text_rect.size = self._get_text_rect_required_size()
58
- w, h = self.text_rect.size
59
- outline_offset = self._get_outline_offset()
60
-
61
58
  size = (
62
59
  max(
63
- self.indicator.rect.w,
64
- w + self.font_object.point_size + (self.gap if self.text else 0),
60
+ self.indicator.get_min_required_size()[0],
61
+ self.text_rect.w + self.font_object.point_size + (self.gap if self.text else 0),
65
62
  ),
66
- self.text_rect.h + outline_offset[1],
63
+ self.text_rect.h,
67
64
  )
68
65
  return self.inflate_rect_by_padding((0, 0, *size)).size
69
66
 
70
67
  def _build_layout(self) -> None:
71
-
72
68
  gap = self.gap if self.text else 0
73
-
74
69
  self.text_rect.size = self._get_text_rect_required_size()
75
70
 
76
- outline_offset = self._get_outline_offset()
71
+ #right part size
72
+ right_part_height = min(self.text_rect.h, self.font_object.point_size)
73
+ self.indicator.set_size_if_autoresize((right_part_height,right_part_height))
77
74
 
78
- tmp_rect = pygame.FRect(
79
- 0, 0,
80
- self.text_rect.w + gap + self.indicator.rect.w + outline_offset[0],
81
- self.text_rect.h + outline_offset[1]
75
+ #join left and right
76
+ joined_rect = pygame.FRect(
77
+ 0, 0, self.text_rect.w + gap + self.indicator.rect.w, self.text_rect.h
82
78
  )
83
79
 
84
- point_size = min(tmp_rect.h, self.font_object.point_size)
85
- self.indicator.set_size_if_autoresize((point_size,point_size))
86
80
 
87
81
  if self.autoresize_h or self.autoresize_w:
88
- target_rect = self.inflate_rect_by_padding(tmp_rect)
82
+ target_rect = self.inflate_rect_by_padding(joined_rect)
89
83
  if not self.autoresize_w:
90
84
  target_rect.w = self.rect.w
91
85
  if not self.autoresize_h:
@@ -95,14 +89,15 @@ class Toggle(Button):
95
89
  self.build()
96
90
  return
97
91
 
92
+ # ------------------------------------ size is ok
98
93
 
94
+ offset = self._get_outline_offset() if self.show_text_outline else (0,0)
99
95
  padded_rect = self.get_padded_rect()
100
96
  padded_relative = padded_rect.move(-self.rect.x, -self.rect.y)
101
97
 
102
-
98
+ self.align_text(joined_rect, padded_relative.move( offset), self.alignment)
99
+ self.text_rect.midleft = joined_rect.midleft
103
100
 
104
- self.align_text(tmp_rect, padded_relative, self.alignment)
105
- self.text_rect.midleft = tmp_rect.midleft
106
101
  if self.text:
107
102
  match self.spacing:
108
103
  case bf.spacing.MAX:
@@ -110,5 +105,8 @@ class Toggle(Button):
110
105
  case bf.spacing.MIN:
111
106
  gap = 0
112
107
 
113
- self.indicator.rect.x = self.rect.x + self.text_rect.right + gap
114
- self.indicator.rect.centery = self.rect.y + padded_relative.top + self.text_rect.h // 2
108
+ pos = self.text_rect.move(
109
+ self.rect.x + gap -offset[0],
110
+ self.rect.y + (self.text_rect.h / 2) - (right_part_height/ 2) -offset[1],
111
+ ).topright
112
+ self.indicator.rect.topleft = pos
@@ -35,6 +35,8 @@ class Widget(bf.Entity, metaclass=WidgetMeta):
35
35
  self.is_root: bool = False
36
36
  self.autoresize_w, self.autoresize_h = True, True
37
37
  self.__constraint_iteration = 0
38
+ self.__constraints_to_ignore = []
39
+ self.__constraints_capture = None
38
40
 
39
41
  def show(self) -> Self:
40
42
  self.visit(lambda w: w.set_visible(True))
@@ -110,7 +112,7 @@ class Widget(bf.Entity, metaclass=WidgetMeta):
110
112
  ]
111
113
  return self
112
114
 
113
- def set_parent_scene(self, parent_scene: bf.Scene) -> Self:
115
+ def set_parent_scene(self, parent_scene: bf.Scene | None) -> Self:
114
116
  super().set_parent_scene(parent_scene)
115
117
  if parent_scene is None:
116
118
  bf.StyleManager().remove_widget(self)
@@ -122,8 +124,8 @@ class Widget(bf.Entity, metaclass=WidgetMeta):
122
124
  def set_parent(self, parent: "Widget") -> Self:
123
125
  if parent == self.parent:
124
126
  return self
125
- if self.parent is not None:
126
- self.parent.remove(self)
127
+ # if self.parent is not None and self.parent != parent:
128
+ # self.parent.remove(self)
127
129
  self.parent = parent
128
130
  return self
129
131
 
@@ -194,41 +196,69 @@ class Widget(bf.Entity, metaclass=WidgetMeta):
194
196
  self.constraints = result
195
197
  self.constraints.sort(key=lambda c: c.priority)
196
198
  self.dirty_constraints = True
199
+ self.__constraint_to_ignore = []
200
+
201
+ return self
202
+
203
+
204
+ def remove_constraints(self, *names: str) -> Self:
205
+ for c in self.constraints:
206
+ if c.name in names:
207
+ c.on_removal(self)
208
+ self.constraints = [c for c in self.constraints if c.name not in names]
209
+ self.__constraint_to_ignore = []
197
210
  return self
198
211
 
199
212
  def resolve_constraints(self) -> None:
200
213
  if self.parent is None or not self.constraints:
201
214
  self.dirty_constraints = False
202
215
  return
203
- all_good = False
204
- constraint_iteration = 0
205
- while not all_good:
206
- for constraint in self.constraints:
207
- if not constraint.evaluate(self.parent, self):
208
- constraint.apply(self.parent, self)
209
- # print(constraint.name,"Applied")
210
- constraint_iteration += 1
211
- if all(c.evaluate(self.parent, self) for c in self.constraints):
212
- all_good = True
216
+
217
+ if not self.__constraint_iteration:
218
+ self.__constraints_capture = None
219
+ else:
220
+ capture = tuple([c.priority for c in self.constraints])
221
+ if capture != self.__constraints_capture:
222
+ self.__constraints_capture = capture
223
+ self.__constraint_to_ignore = []
224
+
225
+ constraints = self.constraints.copy()
226
+ # If all are resolved early exit
227
+ if all(c.evaluate(self.parent,self) for c in constraints if c not in self.__constraint_to_ignore):
228
+ self.dirty_constraints = False
229
+ return
230
+
231
+ # # Here there might be a conflict between 2 or more constraints
232
+ # we have to determine which ones causes conflict and ignore the one with least priority
233
+
234
+ stop = False
235
+
236
+ while True:
237
+ stop = True
238
+ # first pass with 2 iterations to sort out the transformative constraints
239
+ for _ in range(2):
240
+ for c in constraints:
241
+ if c in self.__constraints_to_ignore:continue
242
+ if not c.evaluate(self.parent,self) :
243
+ c.apply(self.parent,self)
244
+ # second pass where we check conflicts
245
+ for c in constraints:
246
+ if c in self.__constraints_to_ignore:
247
+ continue
248
+ if not c.evaluate(self.parent,self):
249
+ # first pass invalidated this constraint
250
+ self.__constraints_to_ignore.append(c)
251
+ stop = False
252
+ break
253
+
254
+ if stop:
213
255
  break
214
- elif self.__constraint_iteration > MAX_CONSTRAINTS:
215
- print(
216
- self,
217
- "CONSTRAINTS ERROR",
218
- list(
219
- c.name
220
- for c in self.constraints
221
- if not c.evaluate(self.parent, self)
222
- ),
223
- )
224
- self.dirty_constraints = False
225
- return
226
- # print("DONE")
227
- self.dirty_constraints = False
228
256
 
229
- def remove_constraints(self, *names: str) -> Self:
230
- self.constraints = [c for c in self.constraints if c.name not in names]
231
- return self
257
+ if self.__constraints_to_ignore:
258
+ print("Constraints ignored : ",[str(c) for c in self.__constraints_to_ignore])
259
+
260
+
261
+ self.dirty_constraints = False
232
262
 
233
263
  def has_constraint(self, name: str) -> bool:
234
264
  return any(c.name == name for c in self.constraints)
@@ -236,7 +266,9 @@ class Widget(bf.Entity, metaclass=WidgetMeta):
236
266
  def get_root(self) -> "Root":
237
267
  if self.is_root:
238
268
  return self
239
- return self.parent.get_root()
269
+ if self.parent:
270
+ return self.parent.get_root()
271
+ return None
240
272
 
241
273
  def top_at(self, x: float | int, y: float | int) -> "None|Widget":
242
274
  if self.children:
@@ -251,6 +283,7 @@ class Widget(bf.Entity, metaclass=WidgetMeta):
251
283
  self.children.extend(children)
252
284
  i = len(self.children)
253
285
  for child in children:
286
+
254
287
  child.set_render_order(i).set_parent(self).set_parent_scene(
255
288
  self.parent_scene
256
289
  )
@@ -262,8 +295,8 @@ class Widget(bf.Entity, metaclass=WidgetMeta):
262
295
  def remove(self, *children: "Widget") -> Self:
263
296
  for child in self.children:
264
297
  if child in children:
265
- self.children.remove(child)
266
298
  child.set_parent(None).set_parent_scene(None)
299
+ self.children.remove(child)
267
300
  if self.parent:
268
301
  self.parent.do_sort_children = True
269
302
 
batFramework/manager.py CHANGED
@@ -24,12 +24,24 @@ class Manager(bf.SceneManager):
24
24
  pygame.display.set_icon(surf)
25
25
 
26
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
27
32
  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
+ # 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
+
33
45
 
34
46
  def get_fps(self) -> float:
35
47
  return self._clock.get_fps()
batFramework/object.py CHANGED
@@ -8,7 +8,9 @@ if TYPE_CHECKING:
8
8
 
9
9
 
10
10
  class Object:
11
- __instance_count = 0
11
+ __count = 0
12
+ __available_uid = set()
13
+ __used_uid = set()
12
14
 
13
15
  def __init__(self) -> None:
14
16
  self.rect = pygame.FRect(0, 0, 0, 0)
@@ -16,14 +18,17 @@ class Object:
16
18
  self.parent_scene: bf.Scene | None = None
17
19
  self.debug_color: tuple | str = "red"
18
20
  self.render_order: int = 0
19
- self.uid: int = Object.__instance_count
20
- Object.__instance_count += 1
21
+ self.uid: int = Object.__count
22
+ Object.__used_uid.add(self.uid)
21
23
 
22
- @staticmethod
23
- def new_uid() -> int:
24
- i = Object.__instance_count
25
- Object.__instance_count += 1
26
- return i
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)
27
32
 
28
33
  def set_position(self, x, y) -> Self:
29
34
  self.rect.topleft = x, y
@@ -57,7 +62,11 @@ class Object:
57
62
  pass
58
63
 
59
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
60
68
  self.uid = uid
69
+ Object.__used_uid.add(uid)
61
70
  return self
62
71
 
63
72
  def add_tags(self, *tags) -> Self:
batFramework/particle.py CHANGED
@@ -7,6 +7,10 @@ class Particle:
7
7
  def __init__(self, *args, **kwargs):
8
8
  self.dead = False
9
9
  self.surface = None
10
+ self.generator = None
11
+
12
+ def do_when_added(self):
13
+ pass
10
14
 
11
15
  def update(self, dt):
12
16
  pass
@@ -21,7 +25,15 @@ class Particle:
21
25
  class TimedParticle(Particle):
22
26
  def __init__(self, duration):
23
27
  super().__init__()
24
- self.timer = bf.Timer(duration, end_callback=self.kill).start()
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()
25
37
 
26
38
 
27
39
  class BasicParticle(TimedParticle):
@@ -72,6 +84,7 @@ class ParticleGenerator(bf.Entity):
72
84
  self.particles: list[Particle] = []
73
85
 
74
86
  def get_debug_outlines(self):
87
+ return
75
88
  for particle in self.particles:
76
89
  yield (
77
90
  particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
@@ -79,7 +92,9 @@ class ParticleGenerator(bf.Entity):
79
92
  )
80
93
  yield (self.rect, "cyan")
81
94
 
82
- def add_particle(self, particle):
95
+ def add_particle(self, particle:Particle):
96
+ particle.generator = self
97
+ particle.do_when_added()
83
98
  self.particles.append(particle)
84
99
 
85
100
  def clear(self):
@@ -94,8 +109,7 @@ class ParticleGenerator(bf.Entity):
94
109
  for p in particles_to_remove:
95
110
  self.particles.remove(p)
96
111
 
97
- def draw(self, camera) -> bool:
112
+ def draw(self, camera) -> None:
98
113
  camera.surface.fblits(
99
114
  [(p.surface, camera.world_to_screen(p.rect)) for p in self.particles]
100
115
  )
101
- return len(self.particles)
batFramework/scene.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
  import re
3
- from typing import TYPE_CHECKING, Any
4
3
  from collections import OrderedDict
5
4
  import itertools
6
5
 
6
+ from typing import TYPE_CHECKING, Any
7
7
  if TYPE_CHECKING:
8
8
  from .manager import Manager
9
9
  from .sceneManager import SceneManager
@@ -35,20 +35,49 @@ class SceneManager:
35
35
 
36
36
  def print_status(self):
37
37
  """
38
- Print some information about the current state of the scenes.
38
+ Print detailed information about the current state of the scenes and shared variables.
39
39
  """
40
- print("-" * 40)
41
- print(
42
- "\n".join(
43
- f" {s.name:<30}\t{'Active' if s.active else 'Inactive'}\t{'Visible' if s.visible else 'Invisible'}\tindex= {s.scene_index}"
44
- for s in self.scenes
45
- )
46
- )
47
- print(f"[Debugging] = {self.debug_mode}")
48
- print("---SHARED VARIABLES---")
49
- for name, value in self.shared_variables.items():
50
- print(f"[{str(name)} = {str(value)}]")
51
- print("-" * 40)
40
+
41
+ def format_scene_info(scene):
42
+ status = 'Active' if scene.active else 'Inactive'
43
+ visibility = 'Visible' if scene.visible else 'Invisible'
44
+ return f"{scene.name:<30} | {status:<8} | {visibility:<10} | Index={scene.scene_index}"
45
+
46
+ def format_shared_variable(name, value):
47
+ return f"[{name}] = {value}"
48
+
49
+ print("\n" + "=" * 50)
50
+ print(" SCENE STATUS".center(50))
51
+ print("=" * 50)
52
+
53
+ # Print scene information
54
+ if self.scenes:
55
+ header = f"{'Scene Name':<30} | {'Status':<8} | {'Visibility':<10} | {'Index':<7}"
56
+ print(header)
57
+ print("-" * 50)
58
+ print("\n".join(format_scene_info(s) for s in self.scenes))
59
+ else:
60
+ print("No scenes available.")
61
+
62
+ # Print debugging mode status
63
+ print("\n" + "=" * 50)
64
+ print(" DEBUGGING STATUS".center(50))
65
+ print("=" * 50)
66
+ print(f"[Debugging Mode] = {self.debug_mode}")
67
+
68
+ # Print shared variables
69
+ print("\n" + "=" * 50)
70
+ print(" SHARED VARIABLES".center(50))
71
+ print("=" * 50)
72
+
73
+ if bf.ResourceManager().shared_variables:
74
+ for name, value in bf.ResourceManager().shared_variables.items():
75
+ print(format_shared_variable(name, value))
76
+ else:
77
+ print("No shared variables available.")
78
+
79
+ print("=" * 50 + "\n")
80
+
52
81
 
53
82
  def set_sharedVar(self, name, value) -> None:
54
83
  bf.ResourceManager().set_sharedVar(name,value)
@@ -7,11 +7,11 @@ class StateMachine: ...
7
7
  class State:
8
8
  def __init__(self, name: str) -> None:
9
9
  self.name = name
10
- self.parent_entity: bf.Entity | bf.AnimatedSprite = None
10
+ self.parent: bf.Entity | bf.AnimatedSprite = None
11
11
  self.state_machine: StateMachine = None
12
12
 
13
- def set_parent_entity(self, parent_entity: bf.Entity | bf.AnimatedSprite):
14
- self.parent_entity = parent_entity
13
+ def set_parent(self, parent: bf.Entity | bf.AnimatedSprite):
14
+ self.parent = parent
15
15
 
16
16
  def set_stateMachine(self, stateMachine):
17
17
  self.state_machine = stateMachine
@@ -27,16 +27,19 @@ class State:
27
27
 
28
28
 
29
29
  class StateMachine:
30
- def __init__(self, parent_entity) -> None:
30
+ def __init__(self, parent) -> None:
31
31
  self.states: dict[str, State] = {}
32
- self.parent_entity = parent_entity
32
+ self.parent = parent
33
33
  self.current_state = None
34
34
 
35
35
  def add_state(self, state: State):
36
36
  self.states[state.name] = state
37
- state.set_parent_entity(self.parent_entity)
37
+ state.set_parent(self.parent)
38
38
  state.set_stateMachine(self)
39
39
 
40
+ def remove_state(self,state_name: str):
41
+ self.states.pop(state_name,default=None)
42
+
40
43
  def set_state(self, state_name: str):
41
44
  if state_name in self.states:
42
45
  if self.current_state:
@@ -0,0 +1,2 @@
1
+ from .character import *
2
+ from .states import *
@@ -0,0 +1,44 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from .states import *
4
+
5
+ class Platform2DCharacter(bf.Character):
6
+ def __init__(self):
7
+ super().__init__()
8
+ self.actions = bf.ActionContainer(
9
+ *bf.DirectionalKeyControls(),
10
+ bf.Action("jump").add_key_control(pygame.K_SPACE).set_holding()
11
+ )
12
+ self.on_ground : bool = False
13
+ self.max_jumps = 2
14
+ self.jump_counter = 0
15
+ self.jump_force = 150
16
+ self.speed = 100
17
+ self.acceleration = 30
18
+ self.friction = 0.7
19
+ self.gravity = 300
20
+ self.terminal_velocity = 1000
21
+ self.state_machine.set_state("idle")
22
+
23
+
24
+ def do_setup_animations(self):
25
+ self.add_animation(bf.Animation("idle"))
26
+ self.add_animation(bf.Animation("run"))
27
+ self.add_animation(bf.Animation("jump"))
28
+ self.add_animation(bf.Animation("fall"))
29
+
30
+
31
+ def do_setup_states(self):
32
+ self.state_machine.add_state(Platform2DIdle())
33
+ self.state_machine.add_state(Platform2DRun())
34
+ self.state_machine.add_state(Platform2DJump())
35
+ self.state_machine.add_state(Platform2DFall())
36
+
37
+
38
+
39
+ def do_reset_actions(self) -> None:
40
+ self.actions.reset()
41
+
42
+ def do_process_actions(self, event: pygame.Event) -> None:
43
+ self.actions.process_event(event)
44
+