batframework 1.0.9a11__py3-none-any.whl → 1.0.9a12__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 (73) hide show
  1. batFramework/__init__.py +2 -0
  2. batFramework/action.py +280 -279
  3. batFramework/actionContainer.py +105 -82
  4. batFramework/animatedSprite.py +80 -58
  5. batFramework/animation.py +91 -77
  6. batFramework/audioManager.py +156 -131
  7. batFramework/baseScene.py +249 -240
  8. batFramework/camera.py +245 -317
  9. batFramework/constants.py +57 -51
  10. batFramework/cutscene.py +239 -253
  11. batFramework/cutsceneManager.py +34 -34
  12. batFramework/drawable.py +107 -77
  13. batFramework/dynamicEntity.py +30 -30
  14. batFramework/easingController.py +58 -58
  15. batFramework/entity.py +130 -130
  16. batFramework/enums.py +171 -135
  17. batFramework/fontManager.py +65 -65
  18. batFramework/gui/__init__.py +28 -25
  19. batFramework/gui/animatedLabel.py +90 -89
  20. batFramework/gui/button.py +17 -17
  21. batFramework/gui/clickableWidget.py +244 -244
  22. batFramework/gui/collapseContainer.py +98 -0
  23. batFramework/gui/constraints/__init__.py +1 -1
  24. batFramework/gui/constraints/constraints.py +1066 -980
  25. batFramework/gui/container.py +220 -206
  26. batFramework/gui/debugger.py +140 -130
  27. batFramework/gui/draggableWidget.py +63 -44
  28. batFramework/gui/image.py +61 -58
  29. batFramework/gui/indicator.py +116 -113
  30. batFramework/gui/interactiveWidget.py +243 -239
  31. batFramework/gui/label.py +147 -344
  32. batFramework/gui/layout.py +442 -429
  33. batFramework/gui/meter.py +155 -96
  34. batFramework/gui/radioButton.py +43 -35
  35. batFramework/gui/root.py +228 -228
  36. batFramework/gui/scrollingContainer.py +282 -0
  37. batFramework/gui/selector.py +232 -250
  38. batFramework/gui/shape.py +286 -276
  39. batFramework/gui/slider.py +353 -397
  40. batFramework/gui/style.py +10 -10
  41. batFramework/gui/styleManager.py +49 -54
  42. batFramework/gui/syncedVar.py +43 -49
  43. batFramework/gui/textInput.py +331 -306
  44. batFramework/gui/textWidget.py +308 -0
  45. batFramework/gui/toggle.py +140 -128
  46. batFramework/gui/tooltip.py +35 -30
  47. batFramework/gui/widget.py +546 -521
  48. batFramework/manager.py +131 -134
  49. batFramework/particle.py +118 -118
  50. batFramework/propertyEaser.py +79 -79
  51. batFramework/renderGroup.py +34 -34
  52. batFramework/resourceManager.py +130 -130
  53. batFramework/scene.py +31 -31
  54. batFramework/sceneLayer.py +134 -138
  55. batFramework/sceneManager.py +200 -197
  56. batFramework/scrollingSprite.py +115 -115
  57. batFramework/sprite.py +46 -51
  58. batFramework/stateMachine.py +49 -54
  59. batFramework/templates/__init__.py +2 -1
  60. batFramework/templates/character.py +15 -0
  61. batFramework/templates/controller.py +158 -97
  62. batFramework/templates/stateMachine.py +39 -0
  63. batFramework/tileset.py +46 -46
  64. batFramework/timeManager.py +213 -213
  65. batFramework/transition.py +162 -162
  66. batFramework/triggerZone.py +22 -22
  67. batFramework/utils.py +306 -306
  68. {batframework-1.0.9a11.dist-info → batframework-1.0.9a12.dist-info}/LICENSE +20 -20
  69. {batframework-1.0.9a11.dist-info → batframework-1.0.9a12.dist-info}/METADATA +24 -17
  70. batframework-1.0.9a12.dist-info/RECORD +72 -0
  71. batframework-1.0.9a11.dist-info/RECORD +0 -67
  72. {batframework-1.0.9a11.dist-info → batframework-1.0.9a12.dist-info}/WHEEL +0 -0
  73. {batframework-1.0.9a11.dist-info → batframework-1.0.9a12.dist-info}/top_level.txt +0 -0
batFramework/sprite.py CHANGED
@@ -1,51 +1,46 @@
1
- import batFramework as bf
2
- import pygame
3
- from typing import Self
4
-
5
-
6
- class Sprite(bf.Drawable):
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
+ import batFramework as bf
2
+ import pygame
3
+ from typing import Self
4
+
5
+
6
+ class Sprite(bf.Drawable):
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
+ tmp = self.rect.size
23
+ super().set_size(size)
24
+ if self.rect.size == tmp:
25
+ return self
26
+ self.surface.blit(
27
+ pygame.transform.scale(self.original_surface, self.rect.size), (0, 0)
28
+ )
29
+ return self
30
+
31
+ def from_path(self, path: str) -> Self:
32
+ tmp = bf.ResourceManager().get_image(path, self.convert_alpha)
33
+ if tmp is None:
34
+ return self
35
+ self.original_surface = tmp
36
+ size = self.original_surface.get_size()
37
+ self.set_size(size)
38
+ return self
39
+
40
+ def from_surface(self, surface: pygame.Surface) -> Self:
41
+ if surface is None:
42
+ return self
43
+ self.original_surface = surface
44
+ size = self.original_surface.get_size()
45
+ self.set_size(size)
46
+ return self
@@ -1,54 +1,49 @@
1
- import batFramework as bf
2
-
3
-
4
- class StateMachine: ...
5
-
6
-
7
- class State:
8
- def __init__(self, name: str) -> None:
9
- self.name = name
10
- self.parent: bf.Entity | bf.AnimatedSprite = None
11
- self.state_machine: StateMachine = None
12
-
13
- def set_parent(self, parent: bf.Entity | bf.AnimatedSprite):
14
- self.parent = parent
15
-
16
- def set_stateMachine(self, stateMachine):
17
- self.state_machine = stateMachine
18
-
19
- def update(self, dt):
20
- pass
21
-
22
- def on_enter(self):
23
- pass
24
-
25
- def on_exit(self):
26
- pass
27
-
28
-
29
- class StateMachine:
30
- def __init__(self, parent) -> None:
31
- self.states: dict[str, State] = {}
32
- self.parent = parent
33
- self.current_state = None
34
-
35
- def add_state(self, state: State):
36
- self.states[state.name] = state
37
- state.set_parent(self.parent)
38
- state.set_stateMachine(self)
39
-
40
- def remove_state(self,state_name: str):
41
- self.states.pop(state_name,default=None)
42
-
43
- def set_state(self, state_name: str):
44
- if state_name in self.states:
45
- if self.current_state:
46
- self.current_state.on_exit()
47
- self.current_state = self.states[state_name]
48
- self.current_state.on_enter()
49
-
50
- def get_current_state(self) -> State:
51
- return self.current_state
52
-
53
- def update(self, dt):
54
- self.current_state.update(dt)
1
+ import batFramework as bf
2
+
3
+
4
+ class State:
5
+ def __init__(self, name: str) -> None:
6
+ self.name = name
7
+ self.parent: bf.Entity = None
8
+ self.state_machine: StateMachine = None
9
+
10
+ def set_parent(self, parent: bf.Entity | bf.AnimatedSprite):
11
+ self.parent = parent
12
+
13
+ def set_stateMachine(self, stateMachine):
14
+ self.state_machine = stateMachine
15
+
16
+ def update(self, dt):
17
+ pass
18
+
19
+ def on_enter(self):
20
+ pass
21
+
22
+ def on_exit(self):
23
+ pass
24
+
25
+
26
+ class StateMachine:
27
+ def __init__(self, parent) -> None:
28
+ self.states: dict[str, State] = {}
29
+ self.parent = parent
30
+ self.current_state = None
31
+
32
+ def add_state(self, *states: State):
33
+ for state in states :
34
+ self.states[state.name] = state
35
+ state.set_parent(self.parent)
36
+ state.set_stateMachine(self)
37
+
38
+ def remove_state(self,state_name: str):
39
+ self.states.pop(state_name,default=None)
40
+
41
+ def set_state(self, state_name: str):
42
+ if state_name in self.states:
43
+ if self.current_state:
44
+ self.current_state.on_exit()
45
+ self.current_state = self.states[state_name]
46
+ self.current_state.on_enter()
47
+
48
+ def update(self, dt):
49
+ self.current_state.update(dt)
@@ -1 +1,2 @@
1
- from .controller import *
1
+ from .controller import *
2
+ from .character import *
@@ -0,0 +1,15 @@
1
+ from .stateMachine import AnimatedSprite,AnimatedStateMachine
2
+ from .controller import PlatformController
3
+ import batFramework as bf
4
+ import pygame
5
+
6
+
7
+
8
+ class PlatformCharacter(bf.AnimatedSprite,PlatformController):
9
+ def __init__(self, *args, **kwargs):
10
+ super().__init__(*args, **kwargs)
11
+ self.animachine = AnimatedStateMachine(self)
12
+
13
+
14
+
15
+
@@ -1,97 +1,158 @@
1
- import batFramework as bf
2
- import pygame
3
-
4
- class PlatformController(bf.DynamicEntity):
5
- def __init__(self,*args,**kwargs):
6
- super().__init__(*args,**kwargs)
7
- self.control = bf.ActionContainer()
8
- self.speed = 500
9
- self.acceleration = 100
10
- self.jump_force = -500
11
- self.gravity = 1200
12
- self.on_ground = False
13
- self.friction = 0.7
14
-
15
- def do_reset_actions(self):
16
- self.control.reset()
17
-
18
- def do_process_actions(self, event):
19
- self.control.process_event(event)
20
-
21
- def check_collision_y(self):
22
- pass
23
-
24
- def check_collision_x(self):
25
- pass
26
-
27
- def update(self, dt):
28
- super().update(dt)
29
- self.velocity.x *= self.friction
30
- if abs(self.velocity.x) <= 0.01:
31
- self.velocity.x = 0
32
- if not self.on_ground:
33
- self.velocity.y += self.gravity * dt
34
- if self.control.is_active("left"):
35
- self.velocity.x -= self.acceleration
36
- if self.control.is_active("right"):
37
- self.velocity.x += self.acceleration
38
- if self.on_ground and self.control.is_active("jump") :
39
- self.velocity.y = self.jump_force
40
- self.on_ground = False
41
-
42
- self.velocity.x = pygame.math.clamp(self.velocity.x,-self.speed,self.speed)
43
- self.rect.x += self.velocity.x * dt
44
- self.check_collision_x()
45
- self.rect.y += self.velocity.y * dt
46
- self.check_collision_y()
47
-
48
-
49
-
50
- class TopDownController(bf.DynamicEntity):
51
- def __init__(self,*args,**kwargs):
52
- super().__init__(*args,**kwargs)
53
- self.control = bf.ActionContainer()
54
- self.input_velocity = pygame.Vector2()
55
- self.speed = 500
56
- self.acceleration = 100
57
- self.friction = 0
58
-
59
- def do_reset_actions(self):
60
- self.control.reset()
61
-
62
- def do_process_actions(self, event):
63
- self.control.process_event(event)
64
-
65
- def check_collision_y(self):
66
- pass
67
-
68
- def check_collision_x(self):
69
- pass
70
-
71
- def update(self, dt):
72
- super().update(dt)
73
- self.input_velocity.update(0,0)
74
- self.velocity *= self.friction
75
-
76
- if abs(self.velocity.x) <= 0.01:
77
- self.velocity.x = 0
78
- if self.control.is_active("left"):
79
- self.input_velocity[0] = -self.acceleration
80
- if self.control.is_active("right"):
81
- self.input_velocity[0] = self.acceleration
82
- if self.control.is_active("up"):
83
- self.input_velocity[1] = -self.acceleration
84
- if self.control.is_active("down"):
85
- self.input_velocity[1] = self.acceleration
86
-
87
- if self.input_velocity:
88
- self.input_velocity.normalize_ip()
89
-
90
- self.velocity += self.input_velocity * self.acceleration
91
- self.velocity.clamp_magnitude_ip(self.speed)
92
-
93
-
94
- self.rect.x += self.velocity.x * dt
95
- self.check_collision_x()
96
- self.rect.y += self.velocity.y * dt
97
- self.check_collision_y()
1
+ import batFramework as bf
2
+ import pygame
3
+ from .stateMachine import *
4
+
5
+
6
+
7
+ class PlatformController(bf.DynamicEntity):
8
+ def __init__(self,*args,**kwargs):
9
+ super().__init__(*args,**kwargs)
10
+ self.control = bf.ActionContainer()
11
+ self.speed = 500
12
+ self.acceleration = 100
13
+ self.jump_force = -500
14
+ self.gravity = 1200
15
+ self.terminal_velocity = 1000
16
+ self.on_ground = False
17
+ self.friction = 0.7
18
+
19
+ def reset_actions(self):
20
+ self.control.reset()
21
+
22
+ def process_actions(self, event):
23
+ self.control.process_event(event)
24
+
25
+ def check_collision_y(self):
26
+ pass
27
+
28
+ def check_collision_x(self):
29
+ pass
30
+
31
+ def update(self, dt):
32
+ super().update(dt)
33
+ if (not self.control.is_active("right")) and (not self.control.is_active("left")):
34
+ self.velocity.x *= self.friction
35
+ if abs(self.velocity.x) <= 0.01:
36
+ self.velocity.x = 0
37
+ if not self.on_ground:
38
+ self.velocity.y += self.gravity * dt
39
+ if self.velocity.y > self.terminal_velocity:
40
+ self.velocity.y = self.terminal_velocity
41
+ if self.control.is_active("left"):
42
+ self.velocity.x -= self.acceleration
43
+ if self.control.is_active("right"):
44
+ self.velocity.x += self.acceleration
45
+ if self.on_ground and self.control.is_active("jump") :
46
+ self.velocity.y = self.jump_force
47
+ self.on_ground = False
48
+
49
+ self.velocity.x = pygame.math.clamp(self.velocity.x,-self.speed,self.speed)
50
+ self.rect.y += self.velocity.y * dt
51
+ self.check_collision_y()
52
+ self.rect.x += self.velocity.x * dt
53
+ self.check_collision_x()
54
+
55
+
56
+
57
+ class TopDownController(bf.DynamicEntity):
58
+ def __init__(self,*args,**kwargs):
59
+ super().__init__(*args,**kwargs)
60
+ self.control = bf.ActionContainer()
61
+ self.input_velocity = pygame.Vector2()
62
+ self.speed = 500
63
+ self.acceleration = 100
64
+ self.friction = 0
65
+
66
+ def reset_actions(self):
67
+ self.control.reset()
68
+
69
+ def process_actions(self, event):
70
+ self.control.process_event(event)
71
+
72
+ def check_collision_y(self):
73
+ pass
74
+
75
+ def check_collision_x(self):
76
+ pass
77
+
78
+ def update(self, dt):
79
+ super().update(dt)
80
+ self.input_velocity.update(0,0)
81
+ self.velocity *= self.friction
82
+
83
+ if abs(self.velocity.x) <= 0.01:
84
+ self.velocity.x = 0
85
+ if self.control.is_active("left"):
86
+ self.input_velocity[0] = -self.acceleration
87
+ if self.control.is_active("right"):
88
+ self.input_velocity[0] = self.acceleration
89
+ if self.control.is_active("up"):
90
+ self.input_velocity[1] = -self.acceleration
91
+ if self.control.is_active("down"):
92
+ self.input_velocity[1] = self.acceleration
93
+
94
+ if self.input_velocity:
95
+ self.input_velocity.normalize_ip()
96
+
97
+ self.velocity += self.input_velocity * self.acceleration
98
+ self.velocity.clamp_magnitude_ip(self.speed)
99
+
100
+
101
+ self.rect.x += self.velocity.x * dt
102
+ self.check_collision_x()
103
+ self.rect.y += self.velocity.y * dt
104
+ self.check_collision_y()
105
+
106
+
107
+
108
+
109
+ class CameraController(bf.Entity):
110
+ def __init__(self, *args, **kwargs):
111
+ super().__init__(*args, **kwargs)
112
+ self.origin = None # Previous frame's world mouse pos
113
+ self.mouse_actions = bf.ActionContainer(
114
+ bf.Action("control").add_key_control(pygame.K_LCTRL).set_holding(),
115
+ bf.Action("drag").add_mouse_control(1).set_holding().set_consume_event(True),
116
+ bf.Action("zoom_in").add_mouse_control(4).set_consume_event(True),
117
+ bf.Action("zoom_out").add_mouse_control(5).set_consume_event(True),
118
+ )
119
+
120
+ def reset_actions(self):
121
+ self.mouse_actions.reset()
122
+
123
+ def process_actions(self, event):
124
+ self.mouse_actions.process_event(event)
125
+ if self.mouse_actions["drag"] and event.type == pygame.MOUSEMOTION:
126
+ event.consumed = True
127
+
128
+ def do_update(self, dt):
129
+ cam = self.parent_layer.camera
130
+ if self.mouse_actions["zoom_in"]:
131
+ if self.mouse_actions["control"]:
132
+ cam.rotate_by(10)
133
+ else:
134
+ cam.zoom(cam.zoom_factor * 1.1)
135
+
136
+ elif self.mouse_actions["zoom_out"]:
137
+ if self.mouse_actions["control"]:
138
+ cam.rotate_by(-10)
139
+ else:
140
+ cam.zoom(cam.zoom_factor / 1.1)
141
+
142
+ if self.mouse_actions["drag"]:
143
+ mouse_world = cam.get_mouse_pos()
144
+ cam_pos = cam.world_rect.topleft
145
+
146
+ if self.origin is None:
147
+ self.origin = (mouse_world[0] - cam_pos[0], mouse_world[1] - cam_pos[1])
148
+ else:
149
+ offset = (mouse_world[0] - cam_pos[0], mouse_world[1] - cam_pos[1])
150
+ dx = offset[0] - self.origin[0]
151
+ dy = offset[1] - self.origin[1]
152
+
153
+ cam.move_by(-dx, -dy)
154
+ self.origin = (mouse_world[0] - cam_pos[0], mouse_world[1] - cam_pos[1])
155
+
156
+
157
+ else:
158
+ self.origin = None
@@ -0,0 +1,39 @@
1
+ import batFramework as bf
2
+ import pygame
3
+ from batFramework.stateMachine import State,StateMachine
4
+ from ..animation import Animation
5
+ from ..animatedSprite import AnimatedSprite
6
+
7
+ class AnimatedState(State):
8
+
9
+ def __init__(self, animation:str):
10
+ super().__init__(animation)
11
+ self._transition : dict[str,str] = {}
12
+ self.animation = animation
13
+
14
+ def add_transition(self,dest:str,transition_animation:str):
15
+ self._transition[dest] = transition_animation
16
+
17
+
18
+ class AnimatedStateMachine(StateMachine):
19
+ def __init__(self, parent:AnimatedSprite):
20
+ self.states: dict[str, AnimatedState] = {}
21
+ self.parent:AnimatedSprite = parent
22
+ self.current_state = None
23
+
24
+ def set_state(self, state_name: str,reset_counter:bool = True):
25
+
26
+ if state_name not in self.states:
27
+ return
28
+
29
+ if self.current_state:
30
+ self.current_state.on_exit()
31
+
32
+ if self.current_state and state_name in self.current_state._transition:
33
+ self.parent.set_animation(self.current_state._transition[state_name],reset_counter,0,self.states[state_name].animation)
34
+ else:
35
+ self.parent.set_animation(self.states[state_name].animation,reset_counter)
36
+
37
+ self.current_state = self.states[state_name]
38
+ self.current_state.on_enter()
39
+
batFramework/tileset.py CHANGED
@@ -1,46 +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)
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)