batframework 1.0.8a2__py3-none-any.whl → 1.0.8a3__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 (65) hide show
  1. batFramework/__init__.py +53 -50
  2. batFramework/action.py +126 -99
  3. batFramework/actionContainer.py +53 -9
  4. batFramework/animatedSprite.py +115 -65
  5. batFramework/audioManager.py +69 -26
  6. batFramework/camera.py +259 -69
  7. batFramework/constants.py +16 -54
  8. batFramework/cutscene.py +36 -29
  9. batFramework/cutsceneBlocks.py +37 -42
  10. batFramework/dynamicEntity.py +9 -7
  11. batFramework/easingController.py +58 -0
  12. batFramework/entity.py +48 -97
  13. batFramework/enums.py +113 -0
  14. batFramework/fontManager.py +65 -0
  15. batFramework/gui/__init__.py +10 -2
  16. batFramework/gui/button.py +9 -78
  17. batFramework/gui/clickableWidget.py +219 -0
  18. batFramework/gui/constraints/__init__.py +1 -0
  19. batFramework/gui/constraints/constraints.py +590 -0
  20. batFramework/gui/container.py +174 -32
  21. batFramework/gui/debugger.py +131 -43
  22. batFramework/gui/dialogueBox.py +99 -0
  23. batFramework/gui/draggableWidget.py +40 -0
  24. batFramework/gui/image.py +54 -18
  25. batFramework/gui/indicator.py +38 -21
  26. batFramework/gui/interactiveWidget.py +177 -13
  27. batFramework/gui/label.py +288 -74
  28. batFramework/gui/layout.py +219 -60
  29. batFramework/gui/meter.py +71 -0
  30. batFramework/gui/radioButton.py +84 -0
  31. batFramework/gui/root.py +128 -38
  32. batFramework/gui/shape.py +253 -57
  33. batFramework/gui/slider.py +246 -0
  34. batFramework/gui/style.py +10 -0
  35. batFramework/gui/styleManager.py +48 -0
  36. batFramework/gui/textInput.py +137 -0
  37. batFramework/gui/toggle.py +115 -51
  38. batFramework/gui/widget.py +329 -254
  39. batFramework/manager.py +40 -19
  40. batFramework/object.py +114 -0
  41. batFramework/particle.py +101 -0
  42. batFramework/renderGroup.py +67 -0
  43. batFramework/resourceManager.py +84 -0
  44. batFramework/scene.py +242 -114
  45. batFramework/sceneManager.py +145 -107
  46. batFramework/scrollingSprite.py +115 -0
  47. batFramework/sprite.py +51 -0
  48. batFramework/stateMachine.py +2 -2
  49. batFramework/tileset.py +46 -0
  50. batFramework/time.py +117 -57
  51. batFramework/transition.py +184 -126
  52. batFramework/utils.py +31 -156
  53. batframework-1.0.8a3.dist-info/LICENCE +21 -0
  54. batframework-1.0.8a3.dist-info/METADATA +55 -0
  55. batframework-1.0.8a3.dist-info/RECORD +58 -0
  56. batFramework/debugger.py +0 -48
  57. batFramework/easing.py +0 -71
  58. batFramework/gui/constraints.py +0 -204
  59. batFramework/gui/frame.py +0 -19
  60. batFramework/particles.py +0 -77
  61. batFramework/transitionManager.py +0 -0
  62. batframework-1.0.8a2.dist-info/METADATA +0 -58
  63. batframework-1.0.8a2.dist-info/RECORD +0 -42
  64. {batframework-1.0.8a2.dist-info → batframework-1.0.8a3.dist-info}/WHEEL +0 -0
  65. {batframework-1.0.8a2.dist-info → batframework-1.0.8a3.dist-info}/top_level.txt +0 -0
@@ -1,165 +1,203 @@
1
1
  import batFramework as bf
2
2
  import pygame
3
+ from typing import Self
3
4
 
4
5
 
6
+ def swap(lst, index1, index2):
7
+ lst[index1], lst[index2] = lst[index2], lst[index1]
8
+
5
9
 
6
10
  class SceneManager:
7
- def __init__(self, *initial_scenes: bf.Scene) -> None:
8
- self._debugging = 0
9
- self.sharedVarDict = {}
11
+ def __init__(self) -> None:
12
+ self.scenes: list[bf.Scene] = []
13
+ self.shared_variables: dict = {}
14
+ self.shared_events = {pygame.WINDOWRESIZED}
10
15
 
11
- self.transitions: list[bf.BaseTransition] = []
12
- self.set_sharedVar("is_debugging_func", lambda: self._debugging)
13
- self.set_sharedVar("in_transition", False)
14
16
  self.set_sharedVar("in_cutscene", False)
17
+ self.set_sharedVar("player_has_control", True)
18
+
19
+ self.debug_mode: bf.enums.debugMode = bf.debugMode.HIDDEN
20
+ self.current_transitions: dict[str, bf.transition.Transition] = {}
15
21
 
16
- self._scenes: list[bf.Scene] = list(initial_scenes)
17
- for index,s in enumerate(self._scenes):
18
- s.set_manager(self)
22
+ def init_scenes(self, *initial_scenes):
23
+ for index, s in enumerate(initial_scenes):
19
24
  s.set_scene_index(index)
20
- s.do_when_added()
21
- self.set_scene(self._scenes[0]._name)
25
+ for s in reversed(initial_scenes):
26
+ self.add_scene(s)
27
+ # self.scenes = list(initial_scenes)
28
+ self.set_scene(initial_scenes[0].get_name())
22
29
  self.update_scene_states()
23
30
 
31
+ def set_shared_event(self, event: pygame.Event) -> None:
32
+ """
33
+ Add an event that will be propagated to all active scenes, not just the one on top.
34
+ """
35
+ self.shared_events.add(event)
36
+
24
37
  def print_status(self):
38
+ """
39
+ Print some information about the current state of the scenes.
40
+ """
25
41
  print("-" * 40)
26
- print([(s._name, s._active, s._visible,s.scene_index) for s in self._scenes])
27
- print(f"[Debugging] = {self._debugging}")
42
+ print(
43
+ "\n".join(
44
+ f" {s.name:<30}\t{'Active' if s.active else 'Inactive'}\t{'Visible' if s.visible else 'Invisible'}\tindex= {s.scene_index}"
45
+ for s in self.scenes
46
+ )
47
+ )
48
+ print(f"[Debugging] = {self.debug_mode}")
28
49
  print("---SHARED VARIABLES---")
29
- _ = [
50
+ for name, value in self.shared_variables.items():
30
51
  print(f"[{str(name)} = {str(value)}]")
31
- for name, value in self.sharedVarDict.items()
32
- ]
33
52
  print("-" * 40)
34
53
 
35
54
  def set_sharedVar(self, name, value) -> bool:
36
- self.sharedVarDict[name] = value
55
+ """
56
+ Set a shared variable of any type. This will be accessible (read/write) from any scene
57
+ """
58
+ self.shared_variables[name] = value
37
59
  return True
38
60
 
39
- def get_sharedVar(self, name):
40
- if name not in self.sharedVarDict:
41
- return None
42
- return self.sharedVarDict[name]
43
-
44
- def get_current_scene_name(self)-> str:
45
- return self._scenes[0].name
46
-
47
- def get_current_scene(self)->bf.Scene:
48
- return self._scenes[0]
61
+ def get_sharedVar(self, name, error_value=None):
62
+ """
63
+ Get a shared variable
64
+ """
65
+ return self.shared_variables.get(name, error_value)
66
+
67
+ def get_current_scene_name(self) -> str:
68
+ """get the name of the current scene"""
69
+ return self.scenes[0].get_name()
70
+
71
+ def get_current_scene(self) -> bf.Scene:
72
+ return self.scenes[0]
49
73
 
50
74
  def update_scene_states(self):
51
- self.active_scenes = [s for s in reversed(self._scenes) if s._active]
52
- self.visible_scenes = [s for s in reversed(self._scenes) if s._visible]
75
+ self.active_scenes = [s for s in reversed(self.scenes) if s.active]
76
+ self.visible_scenes = [s for s in reversed(self.scenes) if s.visible]
53
77
 
54
78
  def add_scene(self, scene: bf.Scene):
55
- if scene in self._scenes and not self.has_scene(scene._name):
79
+ if scene in self.scenes and not self.has_scene(scene.name):
56
80
  return
57
81
  scene.set_manager(self)
58
82
  scene.do_when_added()
59
- self._scenes.insert(0, scene)
83
+ self.scenes.insert(0, scene)
60
84
 
61
85
  def remove_scene(self, name: str):
62
- self._scenes = [s for s in self._scenes if s._name != name]
86
+ self.scenes = [s for s in self.scenes if s.name != name]
63
87
 
64
88
  def has_scene(self, name):
65
- return any(name == scene._name for scene in self._scenes)
89
+ return any(name == scene.name for scene in self.scenes)
66
90
 
67
91
  def get_scene(self, name):
68
92
  if not self.has_scene(name):
69
93
  return None
70
- for scene in self._scenes:
71
- if scene._name == name:
94
+ for scene in self.scenes:
95
+ if scene.name == name:
72
96
  return scene
73
97
 
74
- def transition_to_scene(self, dest_scene_name, transition, **kwargs):
75
- if not self.has_scene(dest_scene_name):
76
- return False
77
- source_surf = pygame.Surface(bf.const.RESOLUTION,pygame.SRCALPHA).convert_alpha()
78
- dest_surf = pygame.Surface(bf.const.RESOLUTION,pygame.SRCALPHA).convert_alpha()
79
-
80
- index = kwargs.pop("index",0)
81
- #draw the surfaces
82
- source_scenes = [s for s in self.visible_scenes if s.scene_index >= index and s._visible]
83
- # source_scenes = self.visible_scenes
84
- _ = [s.draw(source_surf) for s in source_scenes]
85
- # self._scenes[index].draw(source_surf)
86
-
87
- # pygame.image.save_extended:(source_surf,"source_surface.png")
88
- self.get_scene(dest_scene_name).draw(dest_surf)
89
- # pygame.image.save_extended(dest_surf,"dest_surface.png")
90
-
91
- # print(f"start transition from {self._scenes[index]._name} to {dest_scene_name}")
92
-
93
- instance: bf.BaseTransition = transition(
94
- source_surf, dest_surf, **kwargs
95
- )
96
- instance.set_scene_index(index)
97
- instance.set_source_name(self._scenes[index]._name)
98
- instance.set_dest_name(dest_scene_name)
99
- self.transitions.append(instance)
100
- self.set_sharedVar("in_transition", True)
101
-
102
- def set_scene(self, name,index=0):
103
- if len(self._scenes)==0 or not self.has_scene(name) or index>=len(self._scenes):return
104
-
105
- target_scene = self.get_scene(name)
106
- old_scene = self._scenes[index]
107
-
108
- # print(f"switch from {old_scene._name} to {target_scene._name} in index {index}")
98
+ def get_scene_at(self, index: int) -> bf.Scene | None:
99
+ if index < 0 or index >= len(self.scenes):
100
+ return None
101
+ return self.scenes[index]
102
+
103
+ def transition_to_scene(
104
+ self,
105
+ scene_name: str,
106
+ transition: bf.transition.Transition = bf.transition.Fade(0.1),
107
+ index: int = 0,
108
+ ):
109
+ target_scene = self.get_scene(scene_name)
110
+ if not target_scene:
111
+ print(f"Scene '{scene_name}' does not exist")
112
+ return
113
+ if len(self.scenes) == 0 or index >= len(self.scenes) or index < 0:
114
+ return
115
+ source_surface = bf.const.SCREEN.copy()
116
+ dest_surface = bf.const.SCREEN.copy()
117
+
118
+ # self.draw(source_surface)
119
+ target_scene.draw(dest_surface)
120
+ target_scene.do_on_enter_early()
121
+ self.get_scene_at(index).do_on_exit_early()
122
+ self.current_transitions = {"scene_name": scene_name, "transition": transition}
123
+ transition.set_start_callback(lambda: self._start_transition(target_scene))
124
+ transition.set_end_callback(lambda: self._end_transition(scene_name, index))
125
+ transition.set_source(source_surface)
126
+ transition.set_dest(dest_surface)
127
+ transition.start()
128
+
129
+ def _start_transition(self, target_scene: bf.Scene):
130
+ target_scene.set_active(True)
131
+ target_scene.set_visible(True)
132
+ self.set_sharedVar("player_has_control", False)
133
+
134
+ def _end_transition(self, scene_name, index):
135
+ self.set_scene(scene_name, index, True)
136
+ self.set_sharedVar("player_has_control", True)
137
+ self.current_transitions.clear()
138
+
139
+ def set_scene(self, scene_name, index=0, ignore_early: bool = False):
140
+ target_scene = self.get_scene(scene_name)
141
+ if not target_scene:
142
+ print(f"'{scene_name}' does not exist")
143
+ return
144
+ if len(self.scenes) == 0 or index >= len(self.scenes) or index < 0:
145
+ return
109
146
 
110
- #switch
111
- old_scene.on_exit()
112
- self.remove_scene(name)
113
- self._scenes.insert(index,target_scene)
114
- _ = [s.set_scene_index(i) for i,s in enumerate(self._scenes)]
147
+ # switch
148
+ if not ignore_early:
149
+ self.scenes[index].do_on_exit_early()
150
+ self.scenes[index].on_exit()
151
+ # re-insert scene at index 0
152
+ self.scenes.remove(target_scene)
153
+ self.scenes.insert(index, target_scene)
154
+ _ = [s.set_scene_index(i) for i, s in enumerate(self.scenes)]
155
+ if not ignore_early:
156
+ self.scenes[index].do_on_enter_early()
115
157
  target_scene.on_enter()
116
158
 
159
+ def cycle_debug_mode(self):
160
+ current_index = self.debug_mode.value
161
+ next_index = (current_index + 1) % len(bf.debugMode)
162
+ return bf.debugMode(next_index)
117
163
 
118
164
  def process_event(self, event: pygame.Event):
119
- if self.transitions:
120
- return
121
165
  keys = pygame.key.get_pressed()
122
166
  if (
123
167
  keys[pygame.K_LCTRL]
168
+ and keys[pygame.K_LSHIFT]
124
169
  and event.type == pygame.KEYDOWN
125
- and event.key == pygame.K_d
126
170
  ):
127
- self._debugging = (self._debugging + 1) % 3
128
- return
129
- elif (
130
- keys[pygame.K_LCTRL]
131
- and event.type == pygame.KEYDOWN
132
- and event.key == pygame.K_p
133
- ):
134
- self.print_status()
135
- self._scenes[0].process_event(event)
171
+ if event.key == pygame.K_d:
172
+ self.debug_mode = self.cycle_debug_mode()
173
+ self.set_sharedVar("debug_mode", self.debug_mode)
174
+ return
175
+ if event.key == pygame.K_p:
176
+ self.print_status()
177
+ return
178
+ if event.type in self.shared_events:
179
+ [s.process_event(event) for s in self.scenes]
180
+ else:
181
+ self.scenes[0].process_event(event)
136
182
 
137
183
  def update(self, dt: float) -> None:
138
- if self.transitions:
139
- transition = self.transitions[0]
140
- transition.update(dt)
141
- if transition.has_ended():
142
- self.set_sharedVar("in_transition", False)
143
- self.set_scene(transition.dest_scene_name,transition.index)
144
-
145
- self.transitions.pop(0)
146
- return
147
184
  for scene in self.active_scenes:
148
185
  scene.update(dt)
149
186
  self.do_update(dt)
150
187
 
151
188
  def do_update(self, dt: float):
152
- return
189
+ pass
153
190
 
154
191
  def draw(self, surface) -> None:
155
- transition_scene_index = -1
156
- visible_scenes = self.visible_scenes.copy()
157
- if self.transitions:
158
- transition_scene_index = self.transitions[0].index
159
-
160
- if transition_scene_index >=0:
161
- self.transitions[0].draw(surface)
162
- visible_scenes = [s for s in visible_scenes if s.scene_index < transition_scene_index]
163
-
164
- for scene in visible_scenes:
192
+ for scene in self.visible_scenes:
165
193
  scene.draw(surface)
194
+ if self.current_transitions:
195
+ self._draw_transition(surface)
196
+
197
+ def _draw_transition(self, surface):
198
+ self.current_transitions["transition"].set_source(surface)
199
+ tmp = bf.const.SCREEN.copy()
200
+ self.get_scene(self.current_transitions["scene_name"]).draw(tmp)
201
+ self.current_transitions["transition"].set_dest(tmp)
202
+ self.current_transitions["transition"].draw(surface)
203
+ return
@@ -0,0 +1,115 @@
1
+ from typing import Self, Iterator
2
+ from pygame.math import Vector2
3
+ import batFramework as bf
4
+ import pygame
5
+
6
+
7
+ class ScrollingSprite(bf.Sprite):
8
+ def __init__(
9
+ self,
10
+ data: pygame.Surface | str,
11
+ size: None | tuple[int, int] = None,
12
+ convert_alpha: bool = True,
13
+ ):
14
+ self.scroll_value = Vector2(0, 0)
15
+ self.auto_scroll = Vector2(0, 0)
16
+
17
+ # Use integer values for the starting points, converted from floating point scroll values
18
+
19
+ super().__init__(data, size, convert_alpha)
20
+ self.original_width, self.original_height = self.original_surface.get_size()
21
+
22
+ def get_debug_outlines(self):
23
+ yield from super().get_debug_outlines()
24
+ for r in self._get_mosaic_rect_list():
25
+ yield r.move(*self.rect.topleft)
26
+
27
+ def set_image(
28
+ self, data: pygame.Surface | str, size: None | tuple[int, int] = None
29
+ ) -> Self:
30
+ super().set_image(data, size)
31
+ self.original_width, self.original_height = self.original_surface.get_size()
32
+ return self
33
+
34
+ def set_autoscroll(self, x: float, y: float) -> Self:
35
+ self.auto_scroll.update(x, y)
36
+ return self
37
+
38
+ def set_scroll(self, x: float = None, y: float = None) -> Self:
39
+ self.scroll_value.update(
40
+ x if x else self.scroll_value.x, y if y else self.scroll_value.y
41
+ )
42
+ return self
43
+
44
+ def scroll(self, x: float, y: float) -> Self:
45
+ self.scroll_value += x, y
46
+ return self
47
+
48
+ def update(self, dt: float) -> None:
49
+ if self.auto_scroll:
50
+ self.scroll(*self.auto_scroll * dt)
51
+ original_width, original_height = self.original_surface.get_size()
52
+
53
+ # Use integer values for the starting points, converted from floating point scroll values
54
+
55
+ if self.scroll_value.x > self.original_width:
56
+ self.scroll_value.x -= self.original_width
57
+ if self.scroll_value.y > self.original_height:
58
+ self.scroll_value.y -= self.original_height
59
+
60
+ super().update(dt)
61
+
62
+ def set_size(self, size: tuple[int | None, int | None]) -> Self:
63
+ size = list(size)
64
+ if size[0] is None:
65
+ size[0] = self.rect.w
66
+ if size[1] is None:
67
+ size[1] = self.rect.h
68
+
69
+ self.surface = pygame.Surface(size).convert_alpha()
70
+ self.rect = self.surface.get_frect(topleft=self.rect.topleft)
71
+ return self
72
+
73
+ def _get_mosaic_rect_list(self) -> Iterator[pygame.Rect]:
74
+ # Use integer values for the starting points, converted from floating point scroll values
75
+ start_x = int(self.scroll_value.x % self.original_width)
76
+ start_y = int(self.scroll_value.y % self.original_height)
77
+
78
+ # Adjust start_x and start_y to begin tiling off-screen to the top-left, covering all visible area
79
+ if start_x != 0:
80
+ start_x -= self.original_width
81
+ if start_y != 0:
82
+ start_y -= self.original_height
83
+
84
+ # Set the region in which to tile
85
+ end_x = self.rect.w
86
+ end_y = self.rect.h
87
+
88
+ # Starting y_position for the inner loop
89
+ y_position = start_y
90
+
91
+ # if self.rect.w-1 < self.scroll_value.x < self.rect.w+1 : print(self.scroll_value.x,int(self.scroll_value.x % self.rect.w),start_x,self.rect.w,original_width)
92
+ # Generate all necessary rectangles
93
+ x = start_x
94
+ while x < end_x:
95
+ y = y_position
96
+ while y < end_y:
97
+ yield pygame.Rect(x, y, self.original_width, self.original_height)
98
+ y += self.original_height
99
+ x += self.original_width
100
+ return self
101
+
102
+ def draw(self, camera: bf.Camera) -> int:
103
+ if not (
104
+ self.visible
105
+ and (self.surface is not None)
106
+ and camera.rect.colliderect(self.rect)
107
+ ):
108
+ return 0
109
+ self.surface.fill((0, 0, 0, 0))
110
+ self.surface.fblits(
111
+ [(self.original_surface, r) for r in self._get_mosaic_rect_list()]
112
+ )
113
+ # pygame.draw.rect(camera.surface,"green",next(self._get_mosaic_rect_list()).move(*self.rect.topleft),1)
114
+ camera.surface.blit(self.surface, camera.world_to_screen(self.rect))
115
+ return 1
batFramework/sprite.py ADDED
@@ -0,0 +1,51 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from typing import Self
4
+
5
+
6
+ class Sprite(bf.Entity):
7
+ def __init__(
8
+ self,
9
+ path=None,
10
+ size: None | tuple[int, int] = None,
11
+ convert_alpha: bool = True,
12
+ ):
13
+ self.original_surface: pygame.Surface = None
14
+
15
+ super().__init__(convert_alpha=convert_alpha)
16
+ if path is not None:
17
+ self.from_path(path)
18
+ if size is not None and self.original_surface:
19
+ self.set_size(self.original_surface.get_size())
20
+
21
+ def set_size(self, size: tuple[float, float]) -> Self:
22
+ if size == self.rect.size:
23
+ return self
24
+ self.rect.size = size
25
+ self.surface = pygame.Surface(
26
+ (int(self.rect.w), int(self.rect.h)), self.surface_flags
27
+ )
28
+ if self.convert_alpha:
29
+ self.surface = self.surface.convert_alpha()
30
+ self.surface.fill((0, 0, 0, 0 if self.convert_alpha else 255))
31
+ self.surface.blit(
32
+ pygame.transform.scale(self.original_surface, self.rect.size), (0, 0)
33
+ )
34
+ return self
35
+
36
+ def from_path(self, path: str) -> Self:
37
+ tmp = bf.ResourceManager().get_image(path, self.convert_alpha)
38
+ if tmp is None:
39
+ return self
40
+ self.original_surface = tmp
41
+ size = self.original_surface.get_size()
42
+ self.set_size(size)
43
+ return self
44
+
45
+ def from_surface(self, surface: pygame.Surface) -> Self:
46
+ if surface is None:
47
+ return self
48
+ self.original_surface = surface
49
+ size = self.original_surface.get_size()
50
+ self.set_size(size)
51
+ return self
@@ -1,8 +1,7 @@
1
1
  import batFramework as bf
2
2
 
3
3
 
4
- class StateMachine:
5
- ...
4
+ class StateMachine: ...
6
5
 
7
6
 
8
7
  class State:
@@ -44,6 +43,7 @@ class StateMachine:
44
43
  self.current_state.on_exit()
45
44
  self.current_state = self.states[state_name]
46
45
  self.current_state.on_enter()
46
+
47
47
  def get_current_state(self) -> State:
48
48
  return self.current_state
49
49
 
@@ -0,0 +1,46 @@
1
+ import pygame
2
+ from .resourceManager import ResourceManager
3
+ import batFramework as bf
4
+
5
+
6
+ class Tileset:
7
+ def __init__(self, source: pygame.Surface, tilesize: tuple[int, int]) -> None:
8
+ self.surface = source
9
+ self.tile_size = tilesize
10
+ self.tile_width = source.get_width() // tilesize[0]
11
+ self.tile_height = source.get_height() // tilesize[1]
12
+ # Create flipped versions of the tileset surface
13
+ self.flipped_surfaces = {
14
+ (False, False): self.surface,
15
+ (False, True): pygame.transform.flip(self.surface, False, True),
16
+ (True, False): pygame.transform.flip(self.surface, True, False),
17
+ (True, True): pygame.transform.flip(self.surface, True, True),
18
+ }
19
+
20
+ # Split each of the surfaces into tiles
21
+ self.tile_dict = {}
22
+ for flip_state, surf in self.flipped_surfaces.items():
23
+ tiles = bf.utils.split_surface(surf, self.tile_size)
24
+ for coord, tile in tiles.items():
25
+ if coord not in self.tile_dict:
26
+ self.tile_dict[coord] = {}
27
+ self.tile_dict[coord][flip_state] = tile
28
+
29
+ def __str__(self) -> str:
30
+ num_tiles = 0
31
+ if self.tile_dict:
32
+ num_tiles = len(self.tile_dict.values())
33
+ return f"{num_tiles} tiles | Tile size : {self.tile_size}"
34
+
35
+ def get_tile(
36
+ self, x: int, y: int, flipX=False, flipY=False
37
+ ) -> pygame.Surface | None:
38
+ if flipX:
39
+ x = self.tile_width - 1 - x
40
+ if flipY:
41
+ y = self.tile_height - 1 - y
42
+ tile_data = self.tile_dict.get((x, y), None)
43
+ if tile_data is None:
44
+ return None
45
+
46
+ return tile_data.get((flipX, flipY), None)