batframework 1.0.9a10__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.
- batFramework/__init__.py +53 -76
- batFramework/action.py +99 -126
- batFramework/actionContainer.py +9 -53
- batFramework/animatedSprite.py +114 -56
- batFramework/audioManager.py +36 -82
- batFramework/camera.py +69 -263
- batFramework/constants.py +53 -29
- batFramework/cutscene.py +109 -243
- batFramework/cutsceneBlocks.py +176 -0
- batFramework/debugger.py +48 -0
- batFramework/dynamicEntity.py +9 -16
- batFramework/easing.py +71 -0
- batFramework/entity.py +85 -92
- batFramework/gui/__init__.py +3 -14
- batFramework/gui/button.py +78 -12
- batFramework/gui/constraints.py +204 -0
- batFramework/gui/container.py +31 -183
- batFramework/gui/debugger.py +43 -126
- batFramework/gui/frame.py +19 -0
- batFramework/gui/image.py +20 -55
- batFramework/gui/indicator.py +22 -95
- batFramework/gui/interactiveWidget.py +12 -229
- batFramework/gui/label.py +77 -311
- batFramework/gui/layout.py +66 -411
- batFramework/gui/root.py +35 -203
- batFramework/gui/shape.py +57 -247
- batFramework/gui/toggle.py +48 -114
- batFramework/gui/widget.py +243 -457
- batFramework/manager.py +29 -113
- batFramework/particles.py +77 -0
- batFramework/scene.py +217 -22
- batFramework/sceneManager.py +129 -161
- batFramework/stateMachine.py +8 -11
- batFramework/time.py +75 -0
- batFramework/transition.py +124 -129
- batFramework/transitionManager.py +0 -0
- batFramework/triggerZone.py +4 -4
- batFramework/utils.py +144 -266
- {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/METADATA +24 -17
- batframework-1.0.10.dist-info/RECORD +43 -0
- batFramework/animation.py +0 -77
- batFramework/baseScene.py +0 -240
- batFramework/cutsceneManager.py +0 -34
- batFramework/drawable.py +0 -77
- batFramework/easingController.py +0 -58
- batFramework/enums.py +0 -135
- batFramework/fontManager.py +0 -65
- batFramework/gui/animatedLabel.py +0 -89
- batFramework/gui/clickableWidget.py +0 -245
- batFramework/gui/constraints/__init__.py +0 -1
- batFramework/gui/constraints/constraints.py +0 -980
- batFramework/gui/draggableWidget.py +0 -44
- batFramework/gui/meter.py +0 -96
- batFramework/gui/radioButton.py +0 -35
- batFramework/gui/selector.py +0 -250
- batFramework/gui/slider.py +0 -397
- batFramework/gui/style.py +0 -10
- batFramework/gui/styleManager.py +0 -54
- batFramework/gui/syncedVar.py +0 -49
- batFramework/gui/textInput.py +0 -306
- batFramework/gui/tooltip.py +0 -30
- batFramework/particle.py +0 -118
- batFramework/propertyEaser.py +0 -79
- batFramework/renderGroup.py +0 -34
- batFramework/resourceManager.py +0 -130
- batFramework/sceneLayer.py +0 -138
- batFramework/scrollingSprite.py +0 -115
- batFramework/sprite.py +0 -51
- batFramework/templates/__init__.py +0 -1
- batFramework/templates/controller.py +0 -97
- batFramework/tileset.py +0 -46
- batFramework/timeManager.py +0 -213
- batframework-1.0.9a10.dist-info/RECORD +0 -67
- {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/LICENSE +0 -0
- {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/WHEEL +0 -0
- {batframework-1.0.9a10.dist-info → batframework-1.0.10.dist-info}/top_level.txt +0 -0
batFramework/gui/textInput.py
DELETED
@@ -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)
|
batFramework/gui/tooltip.py
DELETED
@@ -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
|
-
)
|
batFramework/propertyEaser.py
DELETED
@@ -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
|
batFramework/renderGroup.py
DELETED
@@ -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()
|
batFramework/resourceManager.py
DELETED
@@ -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)
|