batframework 1.0.9a11__py3-none-any.whl → 1.0.10__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 (76) hide show
  1. batFramework/__init__.py +53 -76
  2. batFramework/action.py +99 -126
  3. batFramework/actionContainer.py +9 -53
  4. batFramework/animatedSprite.py +114 -56
  5. batFramework/audioManager.py +36 -82
  6. batFramework/camera.py +69 -263
  7. batFramework/constants.py +53 -29
  8. batFramework/cutscene.py +109 -243
  9. batFramework/cutsceneBlocks.py +176 -0
  10. batFramework/debugger.py +48 -0
  11. batFramework/dynamicEntity.py +9 -16
  12. batFramework/easing.py +71 -0
  13. batFramework/entity.py +85 -92
  14. batFramework/gui/__init__.py +3 -14
  15. batFramework/gui/button.py +78 -12
  16. batFramework/gui/constraints.py +204 -0
  17. batFramework/gui/container.py +31 -188
  18. batFramework/gui/debugger.py +43 -126
  19. batFramework/gui/frame.py +19 -0
  20. batFramework/gui/image.py +20 -55
  21. batFramework/gui/indicator.py +22 -95
  22. batFramework/gui/interactiveWidget.py +12 -229
  23. batFramework/gui/label.py +77 -311
  24. batFramework/gui/layout.py +66 -414
  25. batFramework/gui/root.py +35 -203
  26. batFramework/gui/shape.py +57 -247
  27. batFramework/gui/toggle.py +48 -114
  28. batFramework/gui/widget.py +243 -457
  29. batFramework/manager.py +29 -113
  30. batFramework/particles.py +77 -0
  31. batFramework/scene.py +217 -22
  32. batFramework/sceneManager.py +129 -161
  33. batFramework/stateMachine.py +8 -11
  34. batFramework/time.py +75 -0
  35. batFramework/transition.py +124 -129
  36. batFramework/transitionManager.py +0 -0
  37. batFramework/triggerZone.py +4 -4
  38. batFramework/utils.py +144 -266
  39. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/METADATA +24 -17
  40. batframework-1.0.10.dist-info/RECORD +43 -0
  41. batFramework/animation.py +0 -77
  42. batFramework/baseScene.py +0 -240
  43. batFramework/cutsceneManager.py +0 -34
  44. batFramework/drawable.py +0 -77
  45. batFramework/easingController.py +0 -58
  46. batFramework/enums.py +0 -135
  47. batFramework/fontManager.py +0 -65
  48. batFramework/gui/animatedLabel.py +0 -89
  49. batFramework/gui/clickableWidget.py +0 -244
  50. batFramework/gui/constraints/__init__.py +0 -1
  51. batFramework/gui/constraints/constraints.py +0 -980
  52. batFramework/gui/draggableWidget.py +0 -44
  53. batFramework/gui/meter.py +0 -96
  54. batFramework/gui/radioButton.py +0 -35
  55. batFramework/gui/selector.py +0 -250
  56. batFramework/gui/slider.py +0 -397
  57. batFramework/gui/style.py +0 -10
  58. batFramework/gui/styleManager.py +0 -54
  59. batFramework/gui/syncedVar.py +0 -49
  60. batFramework/gui/textInput.py +0 -306
  61. batFramework/gui/tooltip.py +0 -30
  62. batFramework/particle.py +0 -118
  63. batFramework/propertyEaser.py +0 -79
  64. batFramework/renderGroup.py +0 -34
  65. batFramework/resourceManager.py +0 -130
  66. batFramework/sceneLayer.py +0 -138
  67. batFramework/scrollingSprite.py +0 -115
  68. batFramework/sprite.py +0 -51
  69. batFramework/templates/__init__.py +0 -1
  70. batFramework/templates/controller.py +0 -97
  71. batFramework/tileset.py +0 -46
  72. batFramework/timeManager.py +0 -213
  73. batframework-1.0.9a11.dist-info/RECORD +0 -67
  74. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/LICENSE +0 -0
  75. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/WHEEL +0 -0
  76. {batframework-1.0.9a11.dist-info → batframework-1.0.10.dist-info}/top_level.txt +0 -0
@@ -1,306 +0,0 @@
1
- import batFramework as bf
2
- from typing import Self, Callable
3
- from .label import Label
4
- from .interactiveWidget import InteractiveWidget
5
- import pygame
6
-
7
- def find_next_word(s: str, start_index: int) -> int:
8
- length = len(s)
9
- # Ensure the starting index is within the bounds of the string
10
- if start_index < 0 or start_index >= length:
11
- raise ValueError("Starting index is out of bounds")
12
- index = start_index
13
- # If the start_index is at a space, skip leading spaces
14
- if s[index] in [' ','\n']:
15
- while index < length and s[index] in [' ','\n']:
16
- index += 1
17
- # If we've reached the end of the string
18
- if index >= length:
19
- return -1
20
- else:
21
- # If the start_index is within a word, move to the end of that word
22
- while index < length and s[index] not in [' ','\n']:
23
- index += 1
24
- if index == length:
25
- return index
26
- # Return the index of the start of the next word or -1 if no more words are found
27
- return index if index < length else -1
28
-
29
- def find_prev_word(s: str, start_index: int) -> int:
30
- if start_index <= 0 : return 0
31
- length = len(s)
32
-
33
- # Ensure the starting index is within the bounds of the string
34
- if start_index < 0 or start_index >= length:
35
- raise ValueError("Starting index is out of bounds")
36
-
37
- index = start_index
38
-
39
- # If the start_index is at a space, skip trailing spaces
40
- if s[index] in [' ', '\n']:
41
- while index > 0 and s[index-1] in [' ', '\n']:
42
- index -= 1
43
- # If we've reached the beginning of the string
44
- if index <= 0:
45
- return 0 if s[0] not in [' ', '\n'] else -1
46
- else:
47
- # If the start_index is within a word, move to the start of that word
48
- while index > 0 and s[index-1] not in [' ', '\n']:
49
- index -= 1
50
- if index == 0 and s[index] not in [' ', '\n']:
51
- return 0
52
-
53
- # Return the index of the start of the previous word or -1 if no more words are found
54
- return index if index > 0 or (index == 0 and s[0] not in [' ', '\n']) else -1
55
-
56
-
57
- class TextInput(Label, InteractiveWidget):
58
- def __init__(self) -> None:
59
- self.cursor_position = (0, 0)
60
- self.old_key_repeat = (0, 0)
61
- self.cursor_timer = bf.Timer(0.2, self._cursor_toggle, loop=-1).start()
62
- self.cursor_timer.pause()
63
- self.show_cursor = False
64
- self.on_modify: Callable[[str], str] = None
65
- self.set_focusable(True)
66
- self.set_outline_color("black")
67
- super().__init__("")
68
- self.alignment = bf.alignment.TOPLEFT
69
-
70
- def set_modify_callback(self, callback: Callable[[str], str]) -> Self:
71
- self.on_modify = callback
72
- return self
73
-
74
- def __str__(self) -> str:
75
- return f"TextInput"
76
-
77
- def _cursor_toggle(self, value: bool | None = None):
78
- if value is None:
79
- value = not self.show_cursor
80
- self.show_cursor = value
81
- self.dirty_surface = True
82
-
83
- def do_on_click_down(self, button):
84
- if button != 1:
85
- return False
86
- self.get_focus()
87
- return True
88
-
89
- def do_on_enter(self):
90
- pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_IBEAM)
91
-
92
- def do_on_exit(self):
93
- pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
94
-
95
- def do_on_get_focus(self):
96
- self.cursor_timer.resume()
97
- self._cursor_toggle(True)
98
- self.old_key_repeat = pygame.key.get_repeat()
99
- pygame.key.set_repeat(200, 50)
100
-
101
- def do_on_lose_focus(self):
102
- self.cursor_timer.pause()
103
- self._cursor_toggle(False)
104
- pygame.key.set_repeat(*self.old_key_repeat)
105
-
106
- def get_line(self, line: int) -> str | None:
107
- if line < 0:
108
- return None
109
- lines = self.text.split('\n')
110
- if line >= len(lines):
111
- return None
112
- return lines[line]
113
-
114
- def get_debug_outlines(self):
115
- yield from super().get_debug_outlines()
116
- if self.visible:
117
- # offset = self._get_outline_offset() if self.show_text_outline else (0,0)
118
- # yield (self.text_rect.move(self.rect.x - offset[0] - self.scroll.x,self.rect.y - offset[1] - self.scroll.y), "purple")
119
- yield (self.get_cursor_rect().move(-self.scroll+self.rect.topleft),"green")
120
-
121
-
122
- def get_min_required_size(self) -> tuple[float, float]:
123
- size = self._get_text_rect_required_size()
124
- return self.expand_rect_with_padding(
125
- (0, 0,size[0]+self.get_cursor_rect().w,size[1])
126
- ).size
127
-
128
-
129
- def set_cursor_position(self, position: tuple[int, int]) -> Self:
130
- x, y = position
131
-
132
- lines = self.text.split('\n')
133
- y = max(0, min(y, len(lines) - 1))
134
- line_length = len(lines[y])
135
- x = max(0, min(x, line_length))
136
- self.show_cursor = True
137
- self.cursor_position = (x,y)
138
- self.apply_post_updates(skip_draw=True)
139
- offset = self._get_outline_offset() if self.show_text_outline else (0,0)
140
- padded = self.get_inner_rect().move(-self.rect.x + offset[0], -self.rect.y + offset[1])
141
- self.align_text(self.text_rect,padded,self.alignment)
142
- return self
143
-
144
- def get_cursor_rect(self)->pygame.FRect:
145
- if not self.font_object:
146
- return pygame.FRect(0,0,0,0)
147
-
148
- lines = self.text.split('\n')
149
- line_x, line_y = self.cursor_position
150
-
151
- height = self.font_object.get_linesize()
152
-
153
- cursor_y = self.get_inner_rect().__getattribute__(self.alignment.value)[1] - self.rect.top
154
- cursor_y += line_y * height
155
- cursor_x = self.text_rect.x
156
- cursor_x += self.font_object.size(lines[line_y][:line_x])[0] if line_x > 0 else 0
157
- cursor_rect = pygame.Rect(cursor_x, cursor_y, 1, height-1)
158
- offset = self._get_outline_offset()
159
- cursor_rect.move_ip(offset[0] if self.cursor_position[0] >0 else 0,offset[1] if self.cursor_position[1] > 0 else 0)
160
- return cursor_rect
161
-
162
- def cursor_to_absolute(self, position: tuple[int, int]) -> int:
163
- x, y = position
164
-
165
- y = max(0, min(y, len(self.text.split('\n')) - 1))
166
- lines = self.text.split('\n')
167
- x = max(0, min(x, len(lines[y])))
168
-
169
- absolute_position = sum(len(line) + 1 for line in lines[:y]) + x
170
- return absolute_position
171
-
172
- def absolute_to_cursor(self, absolute: int) -> tuple[int, int]:
173
- text = self.text
174
- lines = text.split('\n')
175
- current_pos = 0
176
-
177
- for line_no, line in enumerate(lines):
178
- if absolute <= current_pos + len(line):
179
- return (absolute - current_pos, line_no)
180
- current_pos += len(line) + 1
181
-
182
- return (len(lines[-1]), len(lines) - 1)
183
-
184
- def do_handle_event(self, event):
185
- if not self.is_focused or event.type not in [pygame.TEXTINPUT, pygame.KEYDOWN]:
186
- return
187
-
188
- text = self.get_text()
189
- current_pos = self.cursor_to_absolute(self.cursor_position)
190
- pressed = pygame.key.get_pressed()
191
-
192
- if event.type == pygame.TEXTINPUT:
193
- # Insert text at the current cursor position
194
- self.set_text(f"{text[:current_pos]}{event.text}{text[current_pos:]}")
195
- self.set_cursor_position(self.absolute_to_cursor(current_pos + len(event.text)))
196
- elif event.type == pygame.KEYDOWN:
197
- match event.key:
198
- case pygame.K_ESCAPE:
199
- self.lose_focus()
200
-
201
- case pygame.K_BACKSPACE if current_pos > 0:
202
- # Remove the character before the cursor
203
- delta = current_pos-1
204
- if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
205
- delta = find_prev_word(self.text,current_pos-1)
206
- if delta <0: delta = 0
207
-
208
- self.set_text(f"{text[:delta]}{text[current_pos:]}")
209
- self.set_cursor_position(self.absolute_to_cursor(delta))
210
- self._cursor_toggle(True)
211
-
212
- case pygame.K_DELETE if current_pos < len(text):
213
- # Remove the character at the cursor
214
- self.set_text(f"{text[:current_pos]}{text[current_pos + 1:]}")
215
- self._cursor_toggle(True)
216
-
217
- case pygame.K_RIGHT:
218
- if current_pos < len(text):
219
- self.handle_cursor_movement(pressed, current_pos, direction="right")
220
- self._cursor_toggle(True)
221
-
222
- case pygame.K_LEFT:
223
- if current_pos > 0:
224
- self.handle_cursor_movement(pressed, current_pos, direction="left")
225
- self._cursor_toggle(True)
226
-
227
- case pygame.K_UP:
228
- # Move cursor up one line
229
- self.set_cursor_position((self.cursor_position[0], self.cursor_position[1] - 1))
230
- self._cursor_toggle(True)
231
-
232
- case pygame.K_DOWN:
233
- # Move cursor down one line
234
- self.set_cursor_position((self.cursor_position[0], self.cursor_position[1] + 1))
235
- self._cursor_toggle(True)
236
-
237
- case pygame.K_RETURN:
238
- # Insert a newline at the current cursor position
239
- self.set_text(f"{text[:current_pos]}\n{text[current_pos:]}")
240
- self.set_cursor_position(self.absolute_to_cursor(current_pos + 1))
241
- self._cursor_toggle(True)
242
- case _ :
243
- if event.unicode:
244
- event.consumed = True
245
- return
246
-
247
- event.consumed = True
248
-
249
- def handle_cursor_movement(self, pressed, current_pos, direction):
250
- if direction == "right":
251
- if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
252
- next_word_pos = find_next_word(self.text, current_pos)
253
- self.set_cursor_position(self.absolute_to_cursor(next_word_pos if next_word_pos != -1 else current_pos + 1))
254
- else:
255
- self.set_cursor_position(self.absolute_to_cursor(current_pos + 1))
256
- elif direction == "left":
257
- if pressed[pygame.K_LCTRL] or pressed[pygame.K_RCTRL]:
258
- prev_word_pos = find_prev_word(self.text, current_pos - 1)
259
- self.set_cursor_position(self.absolute_to_cursor(prev_word_pos if prev_word_pos != -1 else current_pos - 1))
260
- else:
261
- self.set_cursor_position(self.absolute_to_cursor(current_pos - 1))
262
-
263
-
264
- def set_text(self, text: str) -> Self:
265
- if self.on_modify:
266
- text = self.on_modify(text)
267
- return super().set_text(text)
268
-
269
- def _paint_cursor(self) -> None:
270
- if not self.font_object or not self.show_cursor:
271
- return
272
- cursor_rect = self.get_cursor_rect()
273
- cursor_rect.move_ip(-self.scroll)
274
-
275
- if self.show_text_outline:
276
- pygame.draw.rect(self.surface, self.text_outline_color, cursor_rect.inflate(2,2))
277
- pygame.draw.rect(self.surface, self.text_color, cursor_rect)
278
-
279
- def paint(self) -> None:
280
- super().paint()
281
- self._paint_cursor()
282
-
283
- def align_text(
284
- self, text_rect: pygame.FRect, area: pygame.FRect, alignment: bf.alignment
285
- ):
286
- cursor_rect = self.get_cursor_rect()
287
-
288
- if alignment == bf.alignment.LEFT:
289
- alignment = bf.alignment.MIDLEFT
290
- elif alignment == bf.alignment.MIDRIGHT:
291
- alignment = bf.alignment.MIDRIGHT
292
- pos = area.__getattribute__(alignment.value)
293
- text_rect.__setattr__(alignment.value, pos)
294
-
295
-
296
- # if cursor_rect.right > area.right+self.scroll.x:
297
- # self.scroll.x=cursor_rect.right - area.right
298
- # elif cursor_rect.x < self.scroll.x+area.left:
299
- # self.scroll.x= cursor_rect.left - area.left
300
- # self.scroll.x = max(self.scroll.x,0)
301
-
302
- if cursor_rect.bottom > self.scroll.y + area.bottom:
303
- self.scroll.y = cursor_rect.bottom - area.bottom
304
- elif cursor_rect.y < self.scroll.y + area.top:
305
- self.scroll.y = cursor_rect.top - area.top
306
- self.scroll.y = max(self.scroll.y, 0)
@@ -1,30 +0,0 @@
1
- from .label import Label
2
- import batFramework as bf
3
- import sys
4
-
5
- class ToolTip(Label):
6
- def __init__(self, text = ""):
7
- super().__init__(text)
8
- self.fade_in_duration : float = 0.1
9
- self.fade_out_duration : float = 0.1
10
- self.set_render_order(sys.maxsize)
11
-
12
- def __str__(self):
13
- return f"ToolTip('{self.text}')"
14
-
15
- def top_at(self, x, y):
16
- return None
17
-
18
- def fade_in(self):
19
- self.set_visible(True)
20
- bf.PropertyEaser(
21
- self.fade_in_duration,bf.easing.EASE_OUT,
22
- 0,self.parent_scene.name
23
- ).add_custom(self.get_alpha,self.set_alpha,255).start()
24
-
25
- def fade_out(self):
26
- bf.PropertyEaser(
27
- self.fade_out_duration,bf.easing.EASE_IN,
28
- 0,self.parent_scene.name,
29
- lambda : self.set_visible(False)
30
- ).add_custom(self.get_alpha,self.set_alpha,0).start()
batFramework/particle.py DELETED
@@ -1,118 +0,0 @@
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
- self.generator = None
11
-
12
- def do_when_added(self):
13
- pass
14
-
15
- def update(self, dt):
16
- pass
17
-
18
- def kill(self):
19
- self.dead = True
20
-
21
- def update_surface(self):
22
- pass
23
-
24
-
25
- class TimedParticle(Particle):
26
- def __init__(self, duration):
27
- super().__init__()
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()
37
-
38
-
39
- class BasicParticle(TimedParticle):
40
- def __init__(
41
- self,
42
- start_pos: tuple[float, float],
43
- start_vel: tuple[float, float],
44
- duration=1,
45
- color=None,
46
- size: tuple[int, int] = (4, 4),
47
- *args,
48
- **kwargs,
49
- ):
50
- super().__init__(duration)
51
- self.rect = pygame.FRect(0,0, *size)
52
- self.rect.center = start_pos
53
- self.surface = pygame.Surface(size)
54
- self.velocity = Vector2(start_vel)
55
- if color:
56
- self.surface.fill(color)
57
- self.start()
58
-
59
- def start(self):
60
- pass
61
-
62
- def update(self, dt):
63
- super().update(dt)
64
- self.rect.center += self.velocity * dt
65
- self.update_surface()
66
-
67
- def update_surface(self):
68
- self.surface.set_alpha(255 - int(self.timer.get_progression() * 255))
69
-
70
-
71
- class DirectionalParticle(BasicParticle):
72
- def start(self):
73
- self.original_surface = self.surface.copy()
74
-
75
- def update_surface(self):
76
- angle = self.velocity.angle_to(Vector2(1, 0))
77
- self.surface = pygame.transform.rotate(self.original_surface, angle)
78
- super().update_surface()
79
-
80
-
81
- class ParticleGenerator(bf.Drawable):
82
- def __init__(self) -> None:
83
- super().__init__((0, 0))
84
- self.particles: list[Particle] = []
85
- self.count = 0
86
-
87
- def get_debug_outlines(self):
88
- return
89
- for particle in self.particles:
90
- yield (
91
- particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
92
- "blue",
93
- )
94
- yield (self.rect, "cyan")
95
-
96
- def add_particle(self, particle:Particle):
97
- particle.generator = self
98
- particle.do_when_added()
99
- self.particles.append(particle)
100
- self.count += 1
101
-
102
- def clear(self):
103
- self.particles = []
104
- self.count = 0
105
- def update(self, dt: float):
106
- particles_to_remove = []
107
- for particle in self.particles:
108
- particle.update(dt)
109
- if particle.dead:
110
- particles_to_remove.append(particle)
111
- for p in particles_to_remove:
112
- self.particles.remove(p)
113
- self.count -= 1
114
-
115
- def draw(self, camera) -> None:
116
- camera.surface.fblits(
117
- [(p.surface, camera.world_to_screen(p.rect)) for p in self.particles]
118
- )
@@ -1,79 +0,0 @@
1
- from .easingController import EasingController
2
- import pygame
3
- from typing import Callable, Any, Self
4
- import batFramework as bf
5
-
6
- class PropertyEaser(EasingController):
7
- class EasedProperty:
8
- def __init__(self, getter: Callable[[], Any], setter: Callable[[Any], None], end_value: Any, start_value: Any = None):
9
- self.getter = getter
10
- self.setter = setter
11
- self.start_value = start_value if start_value is not None else getter()
12
- self.end_value = end_value
13
-
14
- def interpolate(self, t: float):
15
- a, b = self.start_value, self.end_value
16
- if isinstance(a, (int, float)):
17
- return a + (b - a) * t
18
- elif isinstance(a, pygame.Vector2):
19
- return a.lerp(b, t)
20
- elif isinstance(a, pygame.Color):
21
- return pygame.Color(
22
- round(a.r + (b.r - a.r) * t),
23
- round(a.g + (b.g - a.g) * t),
24
- round(a.b + (b.b - a.b) * t),
25
- round(a.a + (b.a - a.a) * t),
26
- )
27
- else:
28
- raise TypeError(f"Unsupported type for interpolation: {type(a)}")
29
-
30
- def apply(self, t: float):
31
- self.setter(self.interpolate(t))
32
-
33
- def __init__(
34
- self,
35
- duration: float = 1,
36
- easing: bf.easing = bf.easing.LINEAR,
37
- loop: int = 0,
38
- register: str = "global",
39
- end_callback: Callable[[], Any] = None,
40
- ):
41
- self.properties: list[PropertyEaser.EasedProperty] = []
42
-
43
- def update_all(progress):
44
- for prop in self.properties:
45
- prop.apply(progress)
46
-
47
- super().__init__(duration, easing, update_all, end_callback, loop, register)
48
-
49
- def __str__(self):
50
- return f"(PROP){super().__str__()}"
51
-
52
- def add_attr(
53
- self,
54
- obj: Any,
55
- attr: str,
56
- end_value: Any,
57
- start_value: Any = None,
58
- )->Self:
59
- self.properties.append(
60
- PropertyEaser.EasedProperty(
61
- getter=lambda o=obj, a=attr: getattr(o, a),
62
- setter=lambda v, o=obj, a=attr: setattr(o, a, v),
63
- end_value=end_value,
64
- start_value=start_value,
65
- )
66
- )
67
- return self
68
-
69
- def add_custom(
70
- self,
71
- getter: Callable[[], Any],
72
- setter: Callable[[Any], None],
73
- end_value: Any,
74
- start_value: Any = None,
75
- )->Self:
76
- self.properties.append(
77
- PropertyEaser.EasedProperty(getter, setter, end_value, start_value)
78
- )
79
- return self
@@ -1,34 +0,0 @@
1
- import batFramework as bf
2
- import pygame
3
- from typing import Callable, Iterator
4
-
5
-
6
- class RenderGroup(bf.Drawable):
7
- def __init__(
8
- self, entity_iterator: Callable[[], Iterator[bf.Drawable]], blit_flags: int = 0
9
- ) -> None:
10
- super().__init__()
11
- self.entity_iterator = entity_iterator
12
- self.blit_flags = blit_flags
13
- self.set_debug_color("white")
14
-
15
- def draw(self, camera: bf.Camera) -> None:
16
- if not self.visible:
17
- return
18
-
19
- fblits_data = []
20
- for e in self.entity_iterator():
21
- if not getattr(e, "drawn_by_group", False):
22
- # Set flag to skip their individual draw call
23
- e.drawn_by_group = True
24
-
25
- if e.visible and camera.rect.colliderect(e.rect):
26
- fblits_data.append(
27
- (e.surface, (e.rect.x - camera.rect.x, e.rect.y - camera.rect.y))
28
- )
29
-
30
- camera.surface.fblits(fblits_data, self.blit_flags)
31
-
32
- def get_debug_outlines(self):
33
- for e in self.entity_iterator():
34
- yield from e.get_debug_outlines()
@@ -1,130 +0,0 @@
1
- import batFramework as bf
2
- import os
3
- import pygame
4
- import sys
5
- import json
6
- from typing import Any, Callable
7
- from .utils import Singleton
8
- import asyncio
9
-
10
-
11
- if getattr(sys, "frozen", False):
12
- # If the application is run as a bundle, the PyInstaller bootloader
13
- # extends the sys module by a flag frozen=True and sets the app
14
- # path into variable _MEIPASS'.
15
- application_path = sys._MEIPASS
16
- else:
17
- application_path = os.getcwd()
18
-
19
-
20
- class ResourceManager(metaclass=Singleton):
21
- def __init__(self):
22
- self.shared_variables: dict[str, Any] = {}
23
- self.convert_image_cache = {}
24
- self.convert_alpha_image_cache = {}
25
- self.sound_cache = {}
26
- self.RESOURCE_PATH = "."
27
- self.loading_thread = None
28
-
29
- def load_resources(self, path: str, progress_callback: Callable[[float], Any] = None):
30
- """
31
- loads resources from a directory.
32
- Progress is reported through the callback.
33
- Supposed to be asynchronous but don't use it as such yet
34
- """
35
- self.progress_callback = progress_callback
36
-
37
- total_files = sum(
38
- len(files) for _, _, files in os.walk(path) if not any(f.startswith(".") for f in files)
39
- )
40
-
41
- loaded_files = 0
42
-
43
- for root, dirs, files in os.walk(path):
44
- files = [f for f in files if not f.startswith(".")]
45
- dirs[:] = [d for d in dirs if not (d.startswith(".") or d.startswith("__"))]
46
- for file in files:
47
- file_path = os.path.join(root, file)
48
-
49
- # Simulate resource loading
50
- # await asyncio.sleep(0) # Yield control to the event loop
51
-
52
- if file.lower().endswith((".png", ".jpg", ".jpeg", ".gif")):
53
- self.load_image(file_path)
54
- elif file.lower().endswith((".mp3", ".wav", ".ogg")):
55
- bf.AudioManager().load_sound(file.split(".")[0], file_path)
56
- elif file.lower().endswith((".ttf", ".otf")):
57
- bf.FontManager().load_font(file_path, file.split(".")[0])
58
-
59
- loaded_files += 1
60
- # Report progress
61
- # if self.progress_callback:
62
- # self.progress_callback(loaded_files / total_files)
63
-
64
- print(f"Loaded resources in directory: '{path}'")
65
-
66
-
67
- def set_resource_path(self, path: str):
68
- self.RESOURCE_PATH = os.path.join(application_path, path)
69
- print(f"Resource path : '{self.RESOURCE_PATH}'")
70
-
71
- def get_path(self, path: str) -> str:
72
- # Normalize path separators
73
- normalized_path = path.replace("/", os.sep).replace("\\", os.sep)
74
- return os.path.join(self.RESOURCE_PATH, normalized_path)
75
-
76
- def load_image(self, path) -> None:
77
- key = self.get_path(path)
78
- if key in self.convert_image_cache:
79
- return
80
- self.convert_image_cache[key] = pygame.image.load(path).convert()
81
- self.convert_alpha_image_cache[key] = pygame.image.load(path).convert_alpha()
82
-
83
-
84
- def get_image(self, path, convert_alpha: bool = False) -> pygame.Surface | None:
85
- key = self.get_path(path)
86
- return (
87
- self.convert_alpha_image_cache.get(key, None)
88
- if convert_alpha
89
- else self.convert_image_cache.get(key, None)
90
- )
91
-
92
- def load_json_from_file(self, path: str) -> dict | None:
93
- try:
94
- with open(self.get_path(path), "r") as file:
95
- data = json.load(file)
96
- return data
97
- except FileNotFoundError:
98
- print(f"File '{path}' not found")
99
- return None
100
-
101
- def save_json_to_file(self, path: str, data) -> bool:
102
- try:
103
- with open(self.get_path(path), "w") as file:
104
- json.dump(data, file, indent=2)
105
- return True
106
- except FileNotFoundError:
107
- return False
108
-
109
-
110
- def set_sharedVar(self, name, value) -> bool:
111
- """
112
- Set a shared variable of any type. This will be accessible (read/write) from any scene
113
- """
114
- self.shared_variables[name] = value
115
- return True
116
-
117
- def set_sharedVars(self, variables: dict) -> bool:
118
- """
119
- Set multiple shared variables at once. This will be accessible (read/write) from any scene.
120
- """
121
- if not isinstance(variables, dict):
122
- raise ValueError("Input must be a dictionary")
123
- self.shared_variables.update(variables)
124
- return True
125
-
126
- def get_sharedVar(self, name, error_value=None):
127
- """
128
- Get a shared variable
129
- """
130
- return self.shared_variables.get(name, error_value)