batframework 1.0.8a7__py3-none-any.whl → 1.0.8a9__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 (70) hide show
  1. batFramework/__init__.py +51 -68
  2. batFramework/action.py +99 -126
  3. batFramework/actionContainer.py +9 -53
  4. batFramework/animatedSprite.py +82 -141
  5. batFramework/audioManager.py +26 -69
  6. batFramework/camera.py +69 -259
  7. batFramework/constants.py +54 -16
  8. batFramework/cutscene.py +29 -39
  9. batFramework/cutsceneBlocks.py +43 -36
  10. batFramework/debugger.py +48 -0
  11. batFramework/dynamicEntity.py +9 -18
  12. batFramework/easing.py +71 -0
  13. batFramework/entity.py +97 -48
  14. batFramework/gui/__init__.py +2 -10
  15. batFramework/gui/button.py +78 -9
  16. batFramework/gui/constraints.py +204 -0
  17. batFramework/gui/container.py +32 -174
  18. batFramework/gui/debugger.py +43 -131
  19. batFramework/gui/frame.py +19 -0
  20. batFramework/gui/image.py +20 -56
  21. batFramework/gui/indicator.py +21 -38
  22. batFramework/gui/interactiveWidget.py +13 -192
  23. batFramework/gui/label.py +74 -309
  24. batFramework/gui/layout.py +63 -231
  25. batFramework/gui/root.py +38 -134
  26. batFramework/gui/shape.py +57 -237
  27. batFramework/gui/toggle.py +51 -101
  28. batFramework/gui/widget.py +250 -358
  29. batFramework/manager.py +19 -52
  30. batFramework/particles.py +77 -0
  31. batFramework/scene.py +123 -281
  32. batFramework/sceneManager.py +116 -178
  33. batFramework/stateMachine.py +8 -11
  34. batFramework/time.py +58 -145
  35. batFramework/transition.py +124 -195
  36. batFramework/transitionManager.py +0 -0
  37. batFramework/triggerZone.py +1 -1
  38. batFramework/utils.py +147 -112
  39. batframework-1.0.8a9.dist-info/METADATA +53 -0
  40. batframework-1.0.8a9.dist-info/RECORD +42 -0
  41. {batframework-1.0.8a7.dist-info → batframework-1.0.8a9.dist-info}/WHEEL +1 -1
  42. batFramework/character.py +0 -27
  43. batFramework/easingController.py +0 -58
  44. batFramework/enums.py +0 -113
  45. batFramework/fontManager.py +0 -65
  46. batFramework/gui/clickableWidget.py +0 -220
  47. batFramework/gui/constraints/__init__.py +0 -1
  48. batFramework/gui/constraints/constraints.py +0 -815
  49. batFramework/gui/dialogueBox.py +0 -99
  50. batFramework/gui/draggableWidget.py +0 -40
  51. batFramework/gui/meter.py +0 -74
  52. batFramework/gui/radioButton.py +0 -84
  53. batFramework/gui/slider.py +0 -240
  54. batFramework/gui/style.py +0 -10
  55. batFramework/gui/styleManager.py +0 -48
  56. batFramework/gui/textInput.py +0 -247
  57. batFramework/object.py +0 -123
  58. batFramework/particle.py +0 -115
  59. batFramework/renderGroup.py +0 -67
  60. batFramework/resourceManager.py +0 -100
  61. batFramework/scrollingSprite.py +0 -114
  62. batFramework/sprite.py +0 -51
  63. batFramework/templates/__init__.py +0 -2
  64. batFramework/templates/character.py +0 -44
  65. batFramework/templates/states.py +0 -166
  66. batFramework/tileset.py +0 -46
  67. batframework-1.0.8a7.dist-info/LICENCE +0 -21
  68. batframework-1.0.8a7.dist-info/METADATA +0 -43
  69. batframework-1.0.8a7.dist-info/RECORD +0 -62
  70. {batframework-1.0.8a7.dist-info → batframework-1.0.8a9.dist-info}/top_level.txt +0 -0
@@ -1,228 +1,157 @@
1
- import batFramework as bf
2
- from typing import Self
3
1
  import pygame
4
-
5
- """
6
- Both surfaces to transition need to be the same size
7
-
8
- """
9
-
10
-
11
- class Transition:
12
- def __init__(
13
- self, duration: float, easing_function: bf.easing = bf.easing.LINEAR
14
- ) -> None:
15
- """
16
- duration : time in seconds
17
- easing function : controls the progression rate
18
- """
19
- self.duration: float = duration
20
- self.controller = bf.EasingController(
21
- easing_function,
22
- duration,
23
- update_callback=self.update,
24
- end_callback=self.end,
25
- )
26
- self.start_callback = None
27
- self.update_callback = None
28
- self.end_callback = None
29
- self.source: pygame.Surface = None
30
- self.dest: pygame.Surface = None
31
-
32
- def __repr__(self) -> str:
33
- return f"Transition {self.__class__},{self.duration}"
34
-
35
- def set_start_callback(self, func) -> Self:
36
- self.start_callback = func
37
- return self
38
-
39
- def set_update_callback(self, func) -> Self:
40
- self.update_callback = func
41
- return self
42
-
43
- def set_end_callback(self, func) -> Self:
44
- self.end_callback = func
45
- return self
46
-
47
- def set_source(self, surface: pygame.Surface) -> None:
48
- self.source = surface
49
-
50
- def set_dest(self, surface: pygame.Surface) -> None:
51
- self.dest = surface
52
-
53
- def start(self):
54
- if self.controller.has_started():
55
- return
56
- if self.duration:
57
- self.controller.start()
58
- if self.start_callback:
59
- self.start_callback()
60
- return
61
-
62
- self.controller.start()
63
- if self.start_callback:
64
- self.start_callback()
65
- self.controller.end()
66
- self.update(1)
67
- self.end()
68
-
69
- def update(self, progression: float) -> None:
70
- if self.update_callback:
71
- self.update_callback(progression)
72
-
73
- def end(self):
74
- self.controller.stop()
75
- if self.end_callback:
76
- self.end_callback()
77
-
78
- def draw(self, surface: pygame.Surface) -> None:
79
- pass
80
-
81
- def skip(self, no_callback: bool = False):
82
- self.controller.stop()
83
- if self.end_callback and (no_callback == False):
84
- self.end_callback()
2
+ import batFramework as bf
85
3
 
86
4
 
87
- class FadeColor(Transition):
5
+ class BaseTransition:
88
6
  def __init__(
89
7
  self,
90
- color: tuple,
91
- middle_duration: float,
92
- first_duration: float | None = None,
93
- second_duration: float | None = None,
94
- easing_function: bf.easing = bf.easing.LINEAR,
8
+ source_surf: pygame.Surface,
9
+ dest_surf: pygame.Surface,
10
+ duration=100,
11
+ **kwargs,
95
12
  ) -> None:
96
- super().__init__(0, easing_function)
97
- if first_duration is None:
98
- first_duration = middle_duration
99
- if second_duration is None:
100
- second_duration = middle_duration
101
-
102
- self.first = Fade(first_duration)
103
- self.second = Fade(second_duration)
104
- self.color = color
105
- self.middle_duration = middle_duration
13
+ self.source = source_surf
14
+ self.dest = dest_surf
15
+ self.ended = False
16
+ self.source_scene_name = ""
17
+ self.dest_scene_name = ""
18
+ self.duration = duration
106
19
  self.index = 0
107
20
 
108
- self.timer = bf.Timer(middle_duration, self.transition_to_second)
109
- self.first.set_end_callback(self.transition_to_middle)
110
- self.second.set_end_callback(self.transition_to_end)
111
-
112
- def transition_to_middle(self):
113
- self.next_step(self.timer.start)
21
+ def set_scene_index(self,index):
22
+ self.index = index
114
23
 
115
- def transition_to_second(self):
116
- self.next_step(self.second.start)
24
+ def set_source_name(self, name):
25
+ self.source_scene_name = name
117
26
 
118
- def transition_to_end(self):
119
- self.next_step(self.end)
27
+ def set_dest_name(self, name):
28
+ self.dest_scene_name = name
120
29
 
121
- def next_step(self, callback=None) -> None:
122
- self.index += 1
123
- if callback:
124
- callback()
30
+ def update(self, dt):
31
+ pass
125
32
 
126
- def set_source(self, surface) -> None:
127
- super().set_source(surface)
128
- self.first.set_source(surface)
33
+ def draw(self, surface):
34
+ pass
129
35
 
130
- def set_dest(self, surface) -> None:
131
- super().set_dest(surface)
132
- self.second.set_dest(surface)
36
+ def has_ended(self):
37
+ return False
133
38
 
134
- def start(self):
135
- if self.start_callback:
136
- self.start_callback()
39
+ def set_ended(self, val):
40
+ self.ended = val
137
41
 
138
- self.color_surf = pygame.Surface(self.source.get_size())
139
- self.color_surf.fill(self.color)
140
42
 
141
- self.first.set_dest(self.color_surf)
142
- self.second.set_source(self.color_surf)
143
- self.first.start()
43
+ class FadeColorTransition(BaseTransition):
44
+ def __init__(
45
+ self,
46
+ source_surf,
47
+ dest_surf,
48
+ duration=600,
49
+ color_duration=200,
50
+ color=bf.color.CLOUD_WHITE,
51
+ **kwargs,
52
+ ) -> None:
53
+ super().__init__(source_surf, dest_surf, duration)
54
+ self.target_time = duration * 2 + color_duration
55
+ self.color_surf = pygame.Surface((source_surf.get_rect().size)).convert_alpha()
56
+ self.color_surf.fill(color)
57
+ self.ease_out = bf.EasingAnimation(
58
+ easing_function=bf.Easing.EASE_IN,
59
+ duration=(duration-color_duration)//2,
60
+ update_callback = lambda x: self.color_surf.set_alpha(int(255 - (255 * x))),
61
+ end_callback=lambda: self.set_ended(True))
62
+
63
+ self.color_timer = bf.Timer(
64
+ duration=color_duration,
65
+ end_callback=lambda: self.set_state("out"))
66
+ self.ease_in = bf.EasingAnimation(
67
+ easing_function=bf.Easing.EASE_IN,
68
+ duration=(duration-color_duration)//2,
69
+ update_callback=lambda x: self.color_surf.set_alpha(int(255 * x)),
70
+ # update_callback=lambda x: print(x),
71
+ end_callback=lambda: self.set_state("color"))
72
+ self.state = None
73
+
74
+ self.state = "in"
75
+ self.ease_in.start()
76
+
77
+ def set_state(self, state: str):
78
+ self.state = state
79
+ if state == "in":
80
+ self.ease_in.start()
81
+ elif state == "color":
82
+ self.color_timer.start()
83
+ elif state == "out":
84
+ self.ease_out.start()
85
+
86
+ def has_ended(self):
87
+ return self.ended
88
+
89
+ def set_ended(self, val):
90
+ super().set_ended(val)
144
91
 
145
92
  def draw(self, surface):
146
- if self.index == 0:
147
- self.first.draw(surface)
148
- elif self.index == 1:
149
- surface.blit(self.color_surf, (0, 0))
150
- else:
151
- self.second.draw(surface)
93
+ if self.state != "color":
94
+ surface.blit(self.source if self.state == "in" else self.dest, (0, 0))
95
+ surface.blit(self.color_surf, (0, 0))
152
96
 
153
- def skip(self, no_callback: bool = False):
154
- if not no_callback and self.end_callback:
155
- self.end_callback()
156
97
 
157
- self.first.controller.stop()
158
- self.timer.stop()
159
- self.second.controller.stop()
98
+ class FadeTransition(BaseTransition):
99
+ def __init__(self, source_surf, dest_surf, duration=500) -> None:
100
+ super().__init__(source_surf, dest_surf)
101
+ self.anim = bf.EasingAnimation(None,bf.Easing.EASE_IN_OUT,duration,self.update_surface,lambda : self.set_ended(True))
102
+ self.anim.start()
160
103
 
104
+ def update_surface(self,progress):
105
+ self.source.set_alpha(int(255 - (255 * progress)))
106
+ self.dest.set_alpha(int(255 * progress))
161
107
 
162
-
163
- class Fade(Transition):
164
- def end(self):
165
- self.dest.set_alpha(255)
166
- return super().end()
108
+ def has_ended(self):
109
+ return self.ended
167
110
 
168
111
  def draw(self, surface):
169
- dest_alpha = 255 * self.controller.get_value()
170
- self.dest.set_alpha(dest_alpha)
171
112
  surface.blit(self.source, (0, 0))
172
113
  surface.blit(self.dest, (0, 0))
173
114
 
174
115
 
175
- class GlideRight(Transition):
176
- def draw(self, surface):
177
- width = surface.get_width()
178
- source_x = -self.controller.get_value() * width
179
- surface.blit(self.source, (source_x, 0))
180
- surface.blit(self.dest, (width + source_x, 0))
181
-
182
-
183
- class GlideLeft(Transition):
184
- def draw(self, surface):
185
- width = surface.get_width()
186
- source_x = self.controller.get_value() * width
187
- surface.blit(self.source, (source_x, 0))
188
- surface.blit(self.dest, (source_x - width, 0))
189
-
190
-
191
- class CircleOut(Transition):
192
- def start(self):
193
- super().start()
194
- self.circle_surf = self.source.copy()
195
- self.circle_surf.set_colorkey((0, 0, 0))
196
- self.circle_surf.fill((0, 0, 0))
197
- self.surface_width = self.circle_surf.get_width()
198
-
199
- def draw(self, surface):
200
- v = self.controller.get_value()
201
-
202
- radius = self.surface_width * v
203
- pygame.draw.circle(
204
- self.circle_surf, "white", self.circle_surf.get_rect().center, radius
116
+ class SlideTransition(BaseTransition):
117
+ def __init__(
118
+ self,
119
+ source_surf,
120
+ dest_surf,
121
+ duration=1000,
122
+ source_alignment: bf.Alignment = bf.Alignment.BOTTOM,
123
+ easing: bf.Easing = bf.Easing.EASE_IN_OUT,
124
+ **kwargs,
125
+ ) -> None:
126
+ super().__init__(source_surf, dest_surf, duration)
127
+ self.offset = pygame.Vector2(0, 0)
128
+ if source_alignment in [bf.Alignment.TOP, bf.Alignment.BOTTOM]:
129
+ self.offset.y = bf.const.RESOLUTION[1]
130
+ if source_alignment == bf.Alignment.TOP:
131
+ self.offset.y *= -1
132
+ elif source_alignment in [bf.Alignment.LEFT, bf.Alignment.RIGHT]:
133
+ self.offset.x = bf.const.RESOLUTION[0]
134
+ if source_alignment == bf.Alignment.LEFT:
135
+ self.offset.x *= -1
136
+ else:
137
+ self.offset.x = -bf.const.RESOLUTION[0]
138
+ print(
139
+ f"Unsupported Alignment : {source_alignment.value}, set to default : {bf.Alignment.LEFT.value} "
140
+ )
141
+ self.anim = bf.EasingAnimation(
142
+ easing_function=easing,
143
+ duration=duration,
144
+ update_callback =lambda x: self.update_offset(self.offset.lerp((0, 0), x)),
145
+ end_callback =lambda: self.set_ended(True),
205
146
  )
206
- mask = pygame.mask.from_surface(self.circle_surf)
207
- mask.to_surface(surface=surface, setsurface=self.dest, unsetsurface=self.source)
147
+ self.anim.start()
208
148
 
149
+ def update_offset(self, vec):
150
+ self.offset.update(vec)
209
151
 
210
- class CircleIn(Transition):
211
- def start(self):
212
- super().start()
213
- self.circle_surf = self.source.copy()
214
- self.circle_surf.set_colorkey((0, 0, 0))
215
- self.circle_surf.fill((0, 0, 0))
216
- self.surface_width = self.circle_surf.get_width()
152
+ def has_ended(self):
153
+ return self.ended
217
154
 
218
155
  def draw(self, surface):
219
- v = self.controller.get_value()
220
- radius = self.surface_width - (self.surface_width * v)
221
- self.circle_surf.fill((0, 0, 0))
222
- pygame.draw.circle(
223
- self.circle_surf, "white", self.circle_surf.get_rect().center, radius
224
- )
225
- mask = pygame.mask.from_surface(self.circle_surf)
226
- mask.to_surface(surface=surface, setsurface=self.source, unsetsurface=self.dest)
227
-
228
-
156
+ surface.blit(self.source, (0, 0))
157
+ surface.blit(self.dest, self.offset)
File without changes
@@ -4,7 +4,7 @@ import batFramework as bf
4
4
  class TriggerZone(bf.Entity):
5
5
  def __init__(self, size, trigger, callback, active=True) -> None:
6
6
  super().__init__(size, True)
7
- self.set_debug_color(bf.color.RED)
7
+ self.set_debug_color(bf.color.RIVER_BLUE)
8
8
  self.active = active
9
9
  self.callback = callback
10
10
  self.trigger = trigger
batFramework/utils.py CHANGED
@@ -3,14 +3,9 @@ from enum import Enum
3
3
  import os
4
4
  import batFramework as bf
5
5
  import json
6
- from .enums import *
7
- import re
8
- from typing import Callable, TYPE_CHECKING, Any
9
- from functools import cache
10
- if TYPE_CHECKING:
11
- from .object import Object
12
- from .entity import Entity
13
6
 
7
+ MAX_FONT_SIZE = 100
8
+ MIN_FONT_SIZE = 8
14
9
 
15
10
  class Singleton(type):
16
11
  _instances = {}
@@ -21,129 +16,169 @@ class Singleton(type):
21
16
  return cls._instances[cls]
22
17
 
23
18
 
24
- class Utils:
19
+ class Direction(Enum):
20
+ HORIZONTAL = "horizontal"
21
+ VERTICAL = "vertical"
25
22
 
26
- @staticmethod
27
- def split_surface(
28
- surface: pygame.Surface, split_size: tuple[int, int], func=None
29
- ) -> dict[tuple[int, int], pygame.Surface]:
30
- """
31
- Splits a surface into subsurfaces and returns a dictionnary of them
32
- with their tuple coordinates as keys.
33
- Exemple : '(0,0) : Surface'
34
- """
35
- width, height = surface.get_size()
36
- res = {}
37
- for iy, y in enumerate(range(0, height, split_size[1])):
38
- for ix, x in enumerate(range(0, width, split_size[0])):
39
- sub = surface.subsurface((x, y, split_size[0], split_size[1]))
40
-
41
- if func is not None:
42
- sub = func(sub)
43
-
44
- res[(ix, iy)] = sub
45
23
 
46
- return res
24
+ class Alignment(Enum):
25
+ LEFT = "left"
26
+ RIGHT = "right"
27
+ CENTER = "center"
28
+ TOP = "top"
29
+ BOTTOM = "bottom"
47
30
 
48
- @staticmethod
49
- def filter_text(text_mode: textMode):
50
- if text_mode == textMode.ALPHABETICAL:
51
- pattern = re.compile(r"[^a-zA-Z]")
52
- elif text_mode == textMode.NUMERICAL:
53
- pattern = re.compile(r"[^0-9]")
54
- elif text_mode == textMode.ALPHANUMERICAL:
55
- pattern = re.compile(r"[^a-zA-Z0-9]")
56
- else:
57
- raise ValueError("Unsupported text mode")
58
31
 
59
- def filter_function(s: str) -> str:
60
- return pattern.sub("", s)
32
+ class Layout(Enum):
33
+ FILL = "fill"
34
+ FIT = "fit"
61
35
 
62
- return filter_function
63
36
 
37
+ class Utils:
38
+ pygame.font.init()
39
+ FONTS = {}
40
+ tilesets = {}
64
41
 
65
42
  @staticmethod
66
- @cache
67
- def create_spotlight(inside_color, outside_color, radius, radius_stop=None, dest_surf=None,size=None):
68
- """
69
- Draws a circle spotlight centered on a surface
70
- inner color on the center
71
- gradient towards outside color from radius to radius stop
72
- surface background is made transparent
73
- if des_surf is None:
74
- if size is None : size is radius_stop*radius_stop
75
- returns the newly created surface of size 'size' with the spotlight drawn
76
-
77
- """
78
- if radius_stop is None:
79
- radius_stop = radius
80
- diameter = radius_stop * 2
81
-
82
- if dest_surf is None:
83
- if size is None:
84
- size = (diameter,diameter)
85
- dest_surf = pygame.Surface(size, pygame.SRCALPHA)
86
-
87
- dest_surf.fill((0,0,0,0))
88
-
89
-
90
- center = dest_surf.get_rect().center
91
-
92
- if radius_stop != radius:
93
- for r in range(radius_stop, radius - 1, -1):
94
- color = [
95
- inside_color[i] + (outside_color[i] - inside_color[i]) * (r - radius) / (radius_stop - radius)
96
- for i in range(3)
97
- ] + [255] # Preserve the alpha channel as fully opaque
98
- pygame.draw.circle(dest_surf, color, center, r)
99
- else:
100
- pygame.draw.circle(dest_surf, inside_color, center, radius)
101
-
102
- return dest_surf
43
+ def get_path(path: str):
44
+ return os.path.join(bf.const.RESOURCE_PATH, path)
103
45
 
104
46
  @staticmethod
105
- def draw_spotlight(dest_surf:pygame.Surface,inside_color,outside_color,radius,radius_stop=None,center=None):
106
- if radius_stop is None:
107
- radius_stop = radius
108
- center = dest_surf.get_rect().center if center is None else center
109
- if radius_stop != radius:
110
- for r in range(radius_stop, radius - 1, -1):
111
- color = [
112
- inside_color[i] + (outside_color[i] - inside_color[i]) * (r - radius) / (radius_stop - radius)
113
- for i in range(3)
114
- ] + [255]
115
- pygame.draw.circle(dest_surf, color, center, r)
116
- else:
117
- pygame.draw.circle(dest_surf, inside_color, center, radius)
47
+ def load_json_from_file(path: str) -> dict:
48
+ try:
49
+ with open(Utils.get_path(path), "r") as file:
50
+ data = json.load(file)
51
+ return data
52
+ except FileNotFoundError:
53
+ print(f"File '{path}' not found")
54
+ return None
118
55
 
119
56
  @staticmethod
120
- def animate_move(entity:"Object", start_pos : tuple[float,float], end_pos:tuple[float,float])->Callable[[float],None]:
121
- def func(x):
122
- entity.set_center(start_pos[0]+(end_pos[0]-start_pos[0])*x,start_pos[1]+(end_pos[1]-start_pos[1])*x)
123
- return func
124
-
125
- def animate_move_to(entity: "Object", end_pos: tuple[float, float]) -> Callable[[float], None]:
126
- # Start position will be captured once when the animation starts
127
- start_pos = [None]
57
+ def save_json_to_file(path: str, data) -> bool:
58
+ try:
59
+ with open(Utils.get_path(path), "w") as file:
60
+ json.dump(data, file, indent=2)
61
+ return True
62
+ except FileNotFoundError:
63
+ return False
64
+
65
+ @staticmethod
66
+ def init_font(raw_path:str):
67
+ try :
68
+ if raw_path is not None:
69
+ Utils.load_font(raw_path if raw_path else None,None)
70
+ Utils.load_font(raw_path)
71
+ except FileNotFoundError:
72
+ Utils.load_sysfont(raw_path)
73
+ Utils.load_sysfont(raw_path,None)
128
74
 
129
- def update_position(progression: float):
130
- if start_pos[0] is None:
131
- start_pos[0] = entity.rect.center # Capture the start position at the start of the animation
132
75
 
133
- # Calculate new position based on progression
134
- new_x = start_pos[0][0] + (end_pos[0] - start_pos[0][0]) * progression
135
- new_y = start_pos[0][1] + (end_pos[1] - start_pos[0][1]) * progression
76
+ @staticmethod
77
+ def load_font(path:str,name:str=''):
78
+ if path is not None: path = Utils.get_path(path) # convert path if given
79
+ filename = os.path.basename(path).split('.')[0] if path is not None else None # get filename if path is given, else None
80
+ if name != '' : filename = name # if name is not given, name is the filename
81
+ Utils.FONTS[filename] = {}
82
+ # fill the dict
83
+ for size in range(MIN_FONT_SIZE, MAX_FONT_SIZE, 2):
84
+ Utils.FONTS[filename][size] = pygame.font.Font(path,size=size)
85
+
86
+ def load_sysfont(font_name:str,key:str=''):
87
+ if key == '' : key = font_name
88
+ if pygame.font.match_font(font_name) is None:
89
+ raise FileNotFoundError(f"Requested font '{font_namey}' was not found")
90
+ Utils.FONTS[font_name] = {}
91
+
92
+ for size in range(MIN_FONT_SIZE, MAX_FONT_SIZE, 2):
93
+ Utils.FONTS[key][size] = pygame.font.SysFont(font_name,size=size)
94
+
136
95
 
137
- # Set the entity's new position
138
- entity.set_center(new_x, new_y)
96
+ @staticmethod
97
+ def get_font(name:str|None=None,text_size:int=12) -> pygame.Font:
98
+ if not name in Utils.FONTS: return None
99
+ if not text_size in Utils.FONTS[name]: return None
100
+ return Utils.FONTS[name][text_size]
101
+
102
+ class Tileset:
103
+ _flip_cache = {} # {"tileset":tileset,"index","flipX","flipY"}
104
+
105
+ def __init__(self, surface: pygame.Surface, tilesize) -> None:
106
+ self.tile_dict = {}
107
+ self.surface = surface
108
+ self.tile_size = tilesize
109
+ self.split_surface(surface)
110
+
111
+ def split_surface(self, surface: pygame.Surface):
112
+ width, height = surface.get_size()
113
+ num_tiles_x = width // self.tile_size
114
+ num_tiles_y = height // self.tile_size
115
+ # Iterate over the tiles vertically and horizontally
116
+ for y in range(num_tiles_y):
117
+ for x in range(num_tiles_x):
118
+ # Calculate the coordinates of the current tile in the tileset
119
+ tile_x = x * self.tile_size
120
+ tile_y = y * self.tile_size
121
+ # Create a subsurface for the current tile
122
+ tile_surface = surface.subsurface(
123
+ pygame.Rect(tile_x, tile_y, self.tile_size, self.tile_size)
124
+ )
125
+ # Calculate the unique key for the tile (e.g., based on its coordinates)
126
+ tile_key = (x, y)
127
+ # Store the subsurface in the dictionary with the corresponding key
128
+ self.tile_dict[tile_key] = tile_surface
129
+ # print(self.tile_dict)
130
+
131
+ def get_tile(self, x, y, flipX=False, flipY=False) -> pygame.Surface | None:
132
+ if (x, y) not in self.tile_dict:
133
+ return None
134
+ if flipX or flipY:
135
+ key = f"{x}{y}:{flipX}{flipY}"
136
+ if not key in self._flip_cache:
137
+ self._flip_cache[key] = pygame.transform.flip(
138
+ self.tile_dict[(x, y)], flipX, flipY
139
+ )
140
+ return self._flip_cache[key]
141
+ return self.tile_dict[(x, y)]
139
142
 
140
- return update_position
143
+ @staticmethod
144
+ def img_slice(file, cell_width, cell_height, flipX=False) -> list[pygame.Surface]:
145
+ src = pygame.image.load(
146
+ os.path.join(bf.const.RESOURCE_PATH, file)
147
+ ).convert_alpha()
148
+ width, height = src.get_size()
149
+ res = []
150
+ for y in range(0, height, cell_height):
151
+ for x in range(0, width, cell_width):
152
+ sub = src.subsurface((x, y, cell_width, cell_height))
153
+ if flipX:
154
+ sub = pygame.transform.flip(sub, True, False)
155
+
156
+ res.append(sub)
157
+ return res
158
+
159
+ def load_tileset(path: str, name: str, tilesize):
160
+ if name in Utils.tilesets:
161
+ return Utils.tilesets[name]
162
+ else:
163
+ img = pygame.image.load(
164
+ os.path.join(bf.const.RESOURCE_PATH, path)
165
+ ).convert_alpha()
166
+ tileset = Utils.Tileset(img, tilesize)
167
+ Utils.tilesets[name] = tileset
168
+ return tileset
141
169
 
142
170
  @staticmethod
143
- def animate_alpha(entity:"Entity", start : int, end:int)->Callable[[float],None]:
144
- def func(x):
145
- entity.set_alpha(int(pygame.math.clamp(start+(end-start)*x,0,255)))
146
- return func
171
+ def get_tileset(name: str) -> Tileset:
172
+ if name not in Utils.tilesets:
173
+ return None
174
+ return Utils.tilesets[name]
175
+
176
+
147
177
 
148
178
 
149
179
 
180
+ def move_points(delta, *points):
181
+ res = []
182
+ for point in points:
183
+ res.append((point[0] + delta[0], point[1] + delta[1]))
184
+ return res