batframework 0.1.13__py3-none-any.whl → 1.0.1__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 (47) hide show
  1. batFramework/__init__.py +41 -46
  2. batFramework/action.py +42 -20
  3. batFramework/actionContainer.py +43 -4
  4. batFramework/animatedSprite.py +26 -20
  5. batFramework/camera.py +177 -47
  6. batFramework/constants.py +26 -51
  7. batFramework/cutscene.py +15 -15
  8. batFramework/cutsceneBlocks.py +11 -9
  9. batFramework/dynamicEntity.py +7 -6
  10. batFramework/easing.py +28 -23
  11. batFramework/entity.py +87 -49
  12. batFramework/enums.py +14 -0
  13. batFramework/fontManager.py +57 -0
  14. batFramework/gui/__init__.py +2 -2
  15. batFramework/gui/button.py +82 -31
  16. batFramework/gui/constraints.py +137 -104
  17. batFramework/gui/container.py +27 -28
  18. batFramework/gui/debugger.py +92 -42
  19. batFramework/gui/frame.py +15 -15
  20. batFramework/gui/image.py +37 -17
  21. batFramework/gui/indicator.py +18 -14
  22. batFramework/gui/interactiveWidget.py +11 -10
  23. batFramework/gui/label.py +60 -56
  24. batFramework/gui/layout.py +50 -47
  25. batFramework/gui/root.py +43 -30
  26. batFramework/gui/shape.py +34 -41
  27. batFramework/gui/slider.py +5 -0
  28. batFramework/gui/toggle.py +31 -27
  29. batFramework/gui/widget.py +148 -128
  30. batFramework/manager.py +18 -13
  31. batFramework/particles.py +16 -13
  32. batFramework/resourceManager.py +55 -0
  33. batFramework/scene.py +141 -83
  34. batFramework/sceneManager.py +21 -16
  35. batFramework/sprite.py +31 -0
  36. batFramework/stateMachine.py +1 -0
  37. batFramework/tileset.py +7 -9
  38. batFramework/time.py +61 -50
  39. batFramework/transition.py +20 -12
  40. batFramework/utils.py +2 -65
  41. batframework-1.0.1.dist-info/LICENCE +21 -0
  42. {batframework-0.1.13.dist-info → batframework-1.0.1.dist-info}/METADATA +3 -2
  43. batframework-1.0.1.dist-info/RECORD +48 -0
  44. {batframework-0.1.13.dist-info → batframework-1.0.1.dist-info}/WHEEL +1 -1
  45. batFramework/debugger.py +0 -48
  46. batframework-0.1.13.dist-info/RECORD +0 -43
  47. {batframework-0.1.13.dist-info → batframework-1.0.1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
+
3
4
  if TYPE_CHECKING:
4
5
  from .constraints import Constraint
5
6
  from .root import Root
@@ -9,106 +10,110 @@ import batFramework as bf
9
10
  import pygame
10
11
  from math import ceil
11
12
 
12
-
13
+ MAX_CONSTRAINT_ITERATION = 10
13
14
 
14
15
  class Widget(bf.Entity):
15
- def __init__(self,convert_alpha=True)->None:
16
+ def __init__(self, convert_alpha=True) -> None:
16
17
  super().__init__(convert_alpha=convert_alpha)
17
18
  self.autoresize = False
18
- self.parent : None|Self = None
19
- self.is_root :bool = False
20
- self.children : list["Widget"] = []
21
- self.focusable :bool= False
22
- self.constraints : list[Constraint] = []
23
- self.gui_depth : int = 0
24
- if self.surface : self.surface.fill("white")
19
+ self.parent: None | Self = None
20
+ self.is_root: bool = False
21
+ self.children: list["Widget"] = []
22
+ self.focusable: bool = False
23
+ self.constraints: list[Constraint] = []
24
+ self.gui_depth: int = 0
25
+ if self.surface:
26
+ self.surface.fill("white")
25
27
  self.set_debug_color("red")
26
- self.padding :tuple[float|int,...]= (0,0,0,0)
28
+ self.padding: tuple[float | int, ...] = (0, 0, 0, 0)
27
29
 
28
- def set_padding(self,value : float|int|tuple|list)->Self:
30
+ def set_padding(self, value: float | int | tuple | list) -> Self:
29
31
  old_raw_size = (
30
32
  self.rect.w - self.padding[0] - self.padding[2],
31
- self.rect.h - self.padding[1] - self.padding[3]
33
+ self.rect.h - self.padding[1] - self.padding[3],
32
34
  )
33
- if isinstance(value,list) or isinstance(value,tuple):
34
- if len(value) > 4 : pass
35
- elif any(v<0 for v in value) : pass
35
+ if isinstance(value, list) or isinstance(value, tuple):
36
+ if len(value) > 4:
37
+ pass
38
+ elif any(v < 0 for v in value):
39
+ pass
36
40
  elif len(value) == 2:
37
- self.padding = (value[0],value[1],value[0],value[1])
41
+ self.padding = (value[0], value[1], value[0], value[1])
38
42
  else:
39
- self.padding = (*value, *self.padding[len(value):])
43
+ self.padding = (*value, *self.padding[len(value) :])
40
44
  else:
41
- self.padding = (value,)*4
45
+ self.padding = (value,) * 4
42
46
 
43
47
  self.set_size(
44
- old_raw_size[0] + self.padding[0] + self.padding[2],
45
- old_raw_size[1] + self.padding[1] + self.padding[3],
48
+ old_raw_size[0] + self.padding[0] + self.padding[2],
49
+ old_raw_size[1] + self.padding[1] + self.padding[3],
46
50
  )
47
- if self.parent :
48
- self.apply_constraints()
49
- self.parent.children_modified()
50
51
  return self
51
-
52
- def inflate_rect_by_padding(self,rect:pygame.FRect)->pygame.FRect:
52
+
53
+
54
+ def inflate_rect_by_padding(self, rect: pygame.FRect) -> pygame.FRect:
53
55
  return pygame.FRect(
54
56
  rect[0] - self.padding[0],
55
- rect[1] - self.padding[1],
56
- rect[2] + self.padding[0]+self.padding[2],
57
- rect[3] + self.padding[1]+self.padding[3]
57
+ rect[1] - self.padding[1],
58
+ rect[2] + self.padding[0] + self.padding[2],
59
+ rect[3] + self.padding[1] + self.padding[3],
58
60
  )
59
- def get_content_left(self)->float:
61
+ def get_min_required_size(self)->tuple[float,float]:
62
+ return self.rect.size
63
+
64
+ def get_content_left(self) -> float:
60
65
  return self.rect.left + self.padding[0]
61
66
 
62
- def get_content_top(self)->float:
67
+ def get_content_top(self) -> float:
63
68
  return self.rect.top + self.padding[1]
64
69
 
65
- def get_content_right(self)->float:
70
+ def get_content_right(self) -> float:
66
71
  return self.rect.right - self.padding[2]
67
72
 
68
- def get_content_bottom(self)->float:
73
+ def get_content_bottom(self) -> float:
69
74
  return self.rect.bottom - self.padding[3]
70
75
 
71
- def get_content_width(self)->float:
76
+ def get_content_width(self) -> float:
72
77
  return self.rect.w - self.padding[0] - self.padding[2]
73
78
 
74
- def get_content_height(self)->float:
79
+ def get_content_height(self) -> float:
75
80
  return self.rect.h - self.padding[1] - self.padding[3]
76
81
 
77
- def get_content_rect(self)->pygame.FRect:
82
+ def get_content_rect(self) -> pygame.FRect:
78
83
  return pygame.FRect(
79
- self.rect.left + self.padding[0],
80
- self.rect.top + self.padding[1],
84
+ self.rect.left + self.padding[0],
85
+ self.rect.top + self.padding[1],
81
86
  self.get_content_width(),
82
- self.get_content_height()
87
+ self.get_content_height(),
83
88
  )
84
89
 
85
- def get_content_rect_rel(self)->pygame.FRect:
86
- return self.get_content_rect().move(-self.rect.left,-self.rect.top)
90
+ def get_content_rect_rel(self) -> pygame.FRect:
91
+ return self.get_content_rect().move(-self.rect.left, -self.rect.top)
87
92
 
88
- def get_content_center(self)->tuple[float,float]:
93
+ def get_content_center(self) -> tuple[float, float]:
89
94
  return self.get_content_rect().center
90
95
 
91
- def get_depth(self)->int:
92
- if self.is_root or self.parent is None :
96
+ def get_depth(self) -> int:
97
+ if self.is_root or self.parent is None:
93
98
  self.gui_depth = 0
94
99
  else:
95
- self.gui_depth = self.parent.get_depth() + 1
100
+ self.gui_depth = self.parent.get_depth() + 1
96
101
  return self.gui_depth
97
102
 
98
103
  def top_at(self, x: float, y: float) -> "None|Widget":
99
104
  if self.children:
100
105
  for child in reversed(self.children):
101
- r = child.top_at(x,y)
106
+ r = child.top_at(x, y)
102
107
  if r is not None:
103
108
  return r
104
- if self.rect.collidepoint(x,y) and self.visible:
109
+ if self.rect.collidepoint(x, y) and self.visible:
105
110
  return self
106
111
  return None
107
-
108
- def get_constraint(self,name:str)->Constraint|None:
112
+
113
+ def get_constraint(self, name: str) -> Constraint | None:
109
114
  return next((c for c in self.constraints if c.name == name), None)
110
115
 
111
- def add_constraints(self,*constraints:Constraint)->Self:
116
+ def add_constraints(self, *constraints: Constraint) -> Self:
112
117
  for constraint in constraints:
113
118
  c = self.get_constraint(constraint.name)
114
119
  if c is not None:
@@ -117,16 +122,15 @@ class Widget(bf.Entity):
117
122
  self.apply_constraints()
118
123
  return self
119
124
 
120
-
121
- def has_constraint(self,name:str)->bool:
125
+ def has_constraint(self, name: str) -> bool:
122
126
  return any(c.name == name for c in self.constraints)
123
127
 
124
- def apply_all_constraints(self)->None:
125
- # print("APPLY ALL CONSTRAINTS IN ",self.to_string())
128
+ def apply_all_constraints(self) -> None:
129
+ for child in self.children:
130
+ child.apply_all_constraints()
126
131
  self.apply_constraints()
127
- for child in self.children : child.apply_all_constraints()
128
-
129
- def apply_constraints(self, max_iterations: int = 10) -> None:
132
+
133
+ def apply_constraints(self) -> None:
130
134
  if not self.parent:
131
135
  # print(f"Warning : can't apply constraints on {self.to_string()} without parent widget")
132
136
  return
@@ -135,7 +139,7 @@ class Widget(bf.Entity):
135
139
  # Sort constraints based on priority
136
140
  self.constraints.sort(key=lambda c: c.priority)
137
141
 
138
- for iteration in range(max_iterations):
142
+ for iteration in range(MAX_CONSTRAINT_ITERATION):
139
143
  unsatisfied = [] # Initialize a flag
140
144
 
141
145
  for constraint in self.constraints:
@@ -143,61 +147,74 @@ class Widget(bf.Entity):
143
147
  unsatisfied.append(constraint)
144
148
  constraint.apply(self.parent, self)
145
149
  if not unsatisfied:
146
- # data = ''.join(f"\n\t->{c.to_string()}" for c in self.constraints)
147
- # print(self.get_depth()*'\t'+f"Following constraints of {self.to_string()} were all satisfied :{data}")
150
+ # data = ''.join(f"\n\t->{c.to_string()}" for c in self.constraints)
151
+ # print(self.get_depth()*'\t'+f"Following constraints of {self.to_string()} were all satisfied :{data}")
148
152
  break
149
153
  # print(f"pass {iteration}/{max_iterations} : unsatisfied = {';'.join(c.to_string() for c in unsatisfied)}")
150
- if iteration == max_iterations - 1:
151
- raise ValueError(f"[WARNING] Following constraints for {self.to_string()} were not satisfied : \n\t{';'.join([c.to_string() for c in unsatisfied])}")
154
+ if iteration == MAX_CONSTRAINT_ITERATION - 1:
155
+ unsatisfied_constraints = '\n\t'.join([c.to_string() for c in unsatisfied])
156
+ raise ValueError(
157
+ f"[WARNING] Following constraints for {self.to_string()} were not satisfied : \n{unsatisfied_constraints}"
158
+ )
152
159
 
153
160
  # GETTERS
154
- def get_by_tags(self,*tags):
161
+ def get_by_tags(self, *tags :str):
155
162
  for c in self.children:
156
163
  yield from c.get_by_tags(*tags)
157
- if any(self.has_tag(t) for t in tags):
164
+ if any(self.has_tags(t) for t in tags):
158
165
  yield self
159
166
 
160
- def get_root(self)-> Self|Root|None:
161
- if self.is_root: return self
162
- if self.parent_scene is not None : return self.parent_scene.root
163
- return None if self.parent is None else self.parent.get_root()
167
+ def count_children_recursive(self):
168
+ return 1 + sum(child.count_children_recursive() for child in self.children)
169
+
164
170
 
165
- def get_size_int(self)->tuple[int,int]:
166
- return (ceil(self.rect.width),ceil(self.rect.height))
171
+ def propagate_function(self,function):
172
+ function(self)
173
+ for child in self.children:
174
+ child.propagate_function(function)
167
175
 
176
+ def get_root(self) -> Root | None:
177
+ if self.parent_scene is not None:
178
+ return self.parent_scene.root
179
+ return None if self.parent is None else self.parent.get_root()
168
180
 
169
- def get_center(self)->tuple[float,float]:
170
- return self.rect.center
181
+ def get_size_int(self) -> tuple[int, int]:
182
+ return (ceil(self.rect.width), ceil(self.rect.height))
171
183
 
184
+ def get_center(self) -> tuple[float, float]:
185
+ return self.rect.center
172
186
 
173
187
  def get_bounding_box(self):
174
- yield (self.rect,self._debug_color)
175
- yield (self.get_content_rect(),"yellow")
188
+ yield (self.rect, self.debug_color)
189
+ yield (self.get_content_rect(), "yellow")
176
190
  for child in self.children:
177
191
  yield from child.get_bounding_box()
178
192
 
179
- def set_autoresize(self,value:bool)-> Self:
193
+ def set_autoresize(self, value: bool) -> Self:
180
194
  self.autoresize = value
181
195
  self.build()
182
196
  return self
183
197
 
184
- def set_parent(self,parent:Self|None)->None:
198
+ def set_parent(self, parent: Self | None) -> Self:
185
199
  if self.parent:
186
200
  self.parent.remove_child(self)
187
201
  self.parent = parent
188
202
  self.apply_all_constraints()
203
+ return self
204
+
189
205
  # SETTERS
190
-
206
+
191
207
  def set_root(self) -> Self:
192
208
  self.is_root = True
193
209
  return self
194
210
 
195
- def set_parent_scene(self,scene)->None:
211
+ def set_parent_scene(self, scene) -> Self:
196
212
  super().set_parent_scene(scene)
197
- for child in self.children :
213
+ for child in self.children:
198
214
  child.set_parent_scene(scene)
199
-
200
- def set_x(self,x:float)->Self:
215
+ return self
216
+ def set_x(self, x: float) -> Self:
217
+ if x == self.rect.x : return self
201
218
  delta = x - self.rect.x
202
219
  self.rect.x = x
203
220
  for child in self.children:
@@ -205,117 +222,120 @@ class Widget(bf.Entity):
205
222
  self.apply_constraints()
206
223
  return self
207
224
 
208
- def set_y(self,y:float)->Self:
225
+ def set_y(self, y: float) -> Self:
226
+ if y == self.rect.y : return self
209
227
  delta = y - self.rect.y
210
228
  self.rect.y = y
211
229
  for child in self.children:
212
230
  child.set_y(child.rect.y + delta)
213
231
  self.apply_constraints()
214
-
215
232
  return self
216
233
 
217
- def set_position(self,x:float,y:float)->Self:
234
+ def set_position(self, x: float, y: float) -> Self:
235
+ if self.rect.topleft == (x,y) : return self
218
236
  delta_x = x - self.rect.x
219
237
  delta_y = y - self.rect.y
220
- self.rect.topleft = x,y
238
+ self.rect.topleft = x, y
221
239
  for child in self.children:
222
- child.set_position(child.rect.x + delta_x,child.rect.y+delta_y)
240
+ child.set_position(child.rect.x + delta_x, child.rect.y + delta_y)
223
241
  self.apply_constraints()
224
242
  return self
225
-
226
- def set_center(self,x:float,y:float)->Self:
243
+
244
+ def set_center(self, x: float, y: float) -> Self:
245
+ if self.rect.center == (x,y) : return self
227
246
  delta_x = x - self.rect.centerx
228
247
  delta_y = y - self.rect.centery
229
- self.rect.center = x,y
248
+ self.rect.center = x, y
230
249
  for child in self.children:
231
- child.set_position(child.rect.x + delta_x,child.rect.y+delta_y)
250
+ child.set_position(child.rect.x + delta_x, child.rect.y + delta_y)
232
251
  self.apply_constraints()
233
252
  return self
234
253
 
235
-
236
- def set_size(self, width : float, height: float) -> Self:
237
- self.rect.size = (width,height)
254
+ def set_size(self, width: float, height: float) -> Self:
255
+ if self.rect.size == (width,height) : return self
256
+ self.rect.size = (width, height)
238
257
  self.build()
239
-
258
+ self.apply_constraints()
259
+ self.children_modified()
240
260
  return self
241
-
242
261
 
243
262
  # Other Methods
244
263
 
245
- def print_tree(self,ident:int=0)->None:
246
- print('\t'*ident+self.to_string()+(':' if self.children else ''))
247
- for child in self.children :
248
- child.print_tree(ident+1)
264
+ def print_tree(self, ident: int = 0) -> None:
265
+ print("\t" * ident + self.to_string() + (":" if self.children else ""))
266
+ for child in self.children:
267
+ child.print_tree(ident + 1)
249
268
 
250
- def to_string(self)->str:
251
-
269
+ def to_string(self) -> str:
252
270
  return f"{self.to_string_id()}@{*self.rect.topleft,* self.rect.size}"
253
271
 
254
-
255
- def to_string_id(self)->str:
272
+ def to_string_id(self) -> str:
256
273
  return "Widget"
257
274
 
258
-
259
- def do_when_removed(self)->None:
275
+ def do_when_removed(self) -> None:
260
276
  if self.parent_scene and self.parent == self.parent_scene.root:
261
277
  self.set_parent(None)
262
278
 
263
279
  # Methods on children
264
280
 
265
- def add_child(self,*child:"Widget")->None:
266
- for c in child :
281
+ def add_child(self, *child: "Widget") -> None:
282
+ for c in child:
267
283
  self.children.append(c)
268
284
  c.set_parent(self)
269
285
  c.set_parent_scene(self.parent_scene)
270
- c.apply_constraints()
271
- self.apply_constraints()
286
+ c.do_when_added()
287
+ self.apply_all_constraints()
272
288
  self.children_modified()
273
289
 
274
- def remove_child(self,child:Self)->None:
275
- self.children.remove(child)
290
+ def remove_child(self, child: "Widget") -> None:
291
+ try :
292
+ self.children.remove(child)
293
+ except ValueError:
294
+ return
276
295
  child.set_parent(None)
296
+ child.do_when_removed()
277
297
  child.set_parent_scene(None)
278
- self.apply_constraints()
298
+ self.apply_all_constraints()
279
299
  self.children_modified()
280
300
 
281
301
 
282
-
283
-
284
302
  # if return True -> don't propagate to siblings or parents
285
- def process_event(self, event: pygame.Event)->bool:
286
-
303
+ def process_event(self, event: pygame.Event) -> bool:
287
304
  # First propagate to children
288
305
  for child in self.children:
289
306
  if child.process_event(event):
290
307
  return True
291
- #return True if the method is blocking (no propagation to next children of the scene)
308
+ # return True if the method is blocking (no propagation to next children of the scene)
292
309
  return super().process_event(event)
293
310
 
294
-
295
- def update(self,dt:float):
311
+ def update(self, dt: float):
296
312
  for child in self.children:
297
313
  child.update(dt)
314
+ self.do_update(dt)
298
315
 
299
316
  def draw(self, camera: bf.Camera) -> int:
300
- self.children.sort(key=lambda e: (e.z_depth,e.render_order))
301
- return super().draw(camera) + sum([child.draw(camera) for child in self.children])
317
+ return super().draw(camera) + sum(
318
+ [child.draw(camera) for child in self.children]
319
+ )
302
320
 
303
- def build(self)->None:
321
+ def build(self) -> None:
304
322
  """
305
323
  This function is called each time the widget's surface has to be updated
306
324
  It usually has to be overriden if inherited to suit the needs of the new class
307
325
  """
308
- if not self.surface: return
326
+ if not self.surface:
327
+ return
309
328
  if self.surface.get_size() != self.get_size_int():
310
329
  self.surface = pygame.Surface(self.get_size_int())
311
- self.apply_constraints()
312
- if self.parent :
313
- self.parent.children_modified()
314
330
 
315
- def build_all(self)->None:
331
+
332
+ def build_all(self) -> None:
333
+ self.build()
316
334
  for child in self.children:
317
335
  child.build_all()
318
- self.build()
319
- def children_modified(self)->None:
336
+
337
+ def children_modified(self) -> None:
320
338
  if self.parent and not self.is_root:
321
339
  self.parent.children_modified()
340
+
341
+
batFramework/manager.py CHANGED
@@ -2,32 +2,36 @@ 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()
8
+ # random.seed("random")
9
+ self._screen: pygame.Surface|None = bf.const.SCREEN
10
+ self._timeManager = bf.TimeManager()
10
11
  self._cutsceneManager = bf.CutsceneManager(self)
11
12
  self._clock: pygame.Clock = pygame.Clock()
13
+ self.do_pre_init()
12
14
  super().__init__(*initial_scene_list)
13
15
  self.set_sharedVar("clock", self._clock)
14
16
  self.do_init()
15
17
 
16
18
  @staticmethod
17
- def set_icon(path: str):
18
- surf = pygame.image.load(bf.utils.get_path(path)).convert_alpha()
19
+ def set_icon(path: str)->None:
20
+ surf = pygame.image.load(bf.ResourceManager().get_path(path)).convert_alpha()
19
21
  pygame.display.set_icon(surf)
20
22
 
21
- def get_fps(self):
23
+ def get_fps(self)->float:
22
24
  return self._clock.get_fps()
23
25
 
24
- def do_init(self):
26
+ def do_init(self)->None:
25
27
  pass
26
28
 
27
- def stop(self)->None:
29
+ def do_pre_init(self)->None:
30
+ pass
31
+ def stop(self) -> None:
28
32
  self._running = False
29
33
 
30
- def run(self):
34
+ def run(self)->None:
31
35
  self._running = True
32
36
  dt: float = 0
33
37
  while self._running:
@@ -36,13 +40,14 @@ class Manager(bf.SceneManager):
36
40
  self._running = False
37
41
  break
38
42
  if event.type == pygame.VIDEORESIZE:
39
- bf.const.set_resolution((event.w,event.h))
43
+ bf.const.set_resolution((event.w, event.h))
40
44
  self.process_event(event)
41
45
  # update
42
- dt = self._clock.tick(bf.const.FPS if not bf.const.VSYNC else 0) / 1000
43
- dt = min(dt, 0.02)
46
+ # dt = self._clock.tick(bf.const.FPS if not bf.const.VSYNC else 0) / 1000
47
+ dt = self._clock.tick(0 if bf.const.VSYNC else bf.const.FPS) / 1000
48
+ # dt = min(dt, 0.02) dirty fix for dt being too high when window not focused for a long time
44
49
  self._cutsceneManager.update(dt)
45
- self._timeManager.update()
50
+ self._timeManager.update(dt)
46
51
  self.update(dt)
47
52
  # render
48
53
  self.draw(self._screen)
batFramework/particles.py CHANGED
@@ -3,10 +3,8 @@ import pygame
3
3
  from pygame.math import Vector2
4
4
 
5
5
 
6
-
7
6
  class Particle:
8
-
9
- def __init__(self,*args,**kwargs):
7
+ def __init__(self, *args, **kwargs):
10
8
  self.dead = False
11
9
  self.surface = None
12
10
 
@@ -18,18 +16,20 @@ class Particle:
18
16
 
19
17
 
20
18
  class TimedParticle(Particle):
21
- def __init__(self,duration):
19
+ def __init__(self, duration):
22
20
  super().__init__()
23
21
  self.start_time = pygame.time.get_ticks()
24
22
  self.duration = duration
25
23
  self.progression = 0
24
+
26
25
  def update(self, dt):
27
26
  if self.dead:
28
27
  return
29
28
  elapsed_time = pygame.time.get_ticks() - self.start_time
30
29
  self.progression = elapsed_time / self.duration
31
30
  self.dead = elapsed_time >= self.duration
32
-
31
+
32
+
33
33
  class BasicParticle(TimedParticle):
34
34
  def __init__(
35
35
  self,
@@ -37,7 +37,7 @@ class BasicParticle(TimedParticle):
37
37
  start_vel: tuple[float, float],
38
38
  duration=1000,
39
39
  color=None,
40
- size = (4,4)
40
+ size=(4, 4),
41
41
  ):
42
42
  super().__init__()
43
43
  self.rect = pygame.FRect(*start_pos, 0, 0)
@@ -50,12 +50,11 @@ class BasicParticle(TimedParticle):
50
50
  super().update(dt)
51
51
  self.rect.center += self.velocity * dt
52
52
  self.update_surface()
53
-
53
+
54
54
  def update_surface(self):
55
55
  self.surface.set_alpha(255 - int(self.progression * 255))
56
56
 
57
57
 
58
-
59
58
  class ParticleManager(bf.Entity):
60
59
  def __init__(self) -> None:
61
60
  super().__init__(size=bf.const.RESOLUTION)
@@ -68,10 +67,8 @@ class ParticleManager(bf.Entity):
68
67
  def add_particle(self, particle=Particle):
69
68
  self.particles.append(particle)
70
69
 
71
-
72
70
  def clear(self):
73
71
  self.particles = []
74
-
75
72
 
76
73
  def update(self, dt: float):
77
74
  particles_to_remove = []
@@ -83,13 +80,19 @@ class ParticleManager(bf.Entity):
83
80
  self.particles.remove(p)
84
81
 
85
82
  def draw(self, camera) -> bool:
86
- for p in self.particles : p.update_surface()
83
+ for p in self.particles:
84
+ p.update_surface()
87
85
  camera.surface.fblits(
88
86
  [
89
87
  (
90
88
  p.surface,
91
- tuple(round(i * self.z_depth) for i in camera.transpose(self.rect).topleft)
92
- ) for p in self.particles if p.surface
89
+ tuple(
90
+ round(i * self.z_depth)
91
+ for i in camera.transpose(self.rect).topleft
92
+ ),
93
+ )
94
+ for p in self.particles
95
+ if p.surface
93
96
  ]
94
97
  )
95
98
  return len(self.particles)
@@ -0,0 +1,55 @@
1
+ import batFramework as bf
2
+ import os
3
+ import pygame
4
+ import sys
5
+ from .utils import Singleton
6
+ if getattr(sys, "frozen", False):
7
+ # If the application is run as a bundle, the PyInstaller bootloader
8
+ # extends the sys module by a flag frozen=True and sets the app
9
+ # path into variable _MEIPASS'.
10
+ application_path = sys._MEIPASS
11
+ else:
12
+ application_path = os.getcwd()
13
+
14
+
15
+ class ResourceManager(metaclass=Singleton):
16
+ def __init__(self):
17
+ self.convert_image_cache = {}
18
+ self.convert_alpha_image_cache = {}
19
+ self.sound_cache = {}
20
+ self.RESOURCE_PATH = "."
21
+
22
+ def load_dir(self,path)->None:
23
+ for root, dirs, files in os.walk(path):
24
+ for file in files:
25
+ file_path = os.path.join(root, file)
26
+ if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
27
+ self.load_image(file_path)
28
+ elif file.lower().endswith(('.mp3', '.wav')):
29
+ self.load_sound(file_path)
30
+
31
+ def set_resource_path(self,path: str):
32
+ self.RESOURCE_PATH = os.path.join(application_path, path)
33
+ print(f"set resource path to '{self.RESOURCE_PATH}'")
34
+
35
+
36
+ def get_path(self,path: str):
37
+ return os.path.join(self.RESOURCE_PATH, path)
38
+
39
+ def load_image(self,path)->None:
40
+ key = self.get_path(path)
41
+ if key in self.convert_image_cache : return
42
+ self.convert_image_cache[key] = pygame.image.load(path).convert()
43
+ self.convert_alpha_image_cache[key] = pygame.image.load(path).convert_alpha()
44
+
45
+ def load_sound(self,path)->None:
46
+ pass
47
+
48
+ def get_image(self,path,convert_alpha:bool=False):
49
+ key = self.get_path(path)
50
+ return self.convert_alpha_image_cache.get(key,None) if\
51
+ convert_alpha else self.convert_image_cache.get(key,None)
52
+
53
+ def get_sound(self,path):
54
+ return None
55
+