batframework 1.0.9a10__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 -245
  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 -201
  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 -426
  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.9a10.dist-info → batframework-1.0.9a12.dist-info}/LICENSE +20 -20
  69. {batframework-1.0.9a10.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.9a10.dist-info/RECORD +0 -67
  72. {batframework-1.0.9a10.dist-info → batframework-1.0.9a12.dist-info}/WHEEL +0 -0
  73. {batframework-1.0.9a10.dist-info → batframework-1.0.9a12.dist-info}/top_level.txt +0 -0
@@ -1,197 +1,200 @@
1
- import batFramework as bf
2
- import pygame
3
- from typing import Self
4
-
5
- def swap(lst, index1, index2):
6
- lst[index1], lst[index2] = lst[index2], lst[index1]
7
-
8
-
9
- class SceneManager:
10
- def __init__(self) -> None:
11
- self.scenes: list[bf.BaseScene] = []
12
- self.shared_events = {pygame.WINDOWRESIZED}
13
- self.current_transition : tuple[str,bf.transition.Transition,int] | None= None
14
-
15
- def init_scenes(self, *initial_scenes:bf.Scene):
16
- for index, s in enumerate(initial_scenes):
17
- s.set_scene_index(index)
18
- for s in reversed(initial_scenes):
19
- self.add_scene(s)
20
- self.set_scene(initial_scenes[0].get_name())
21
- self.update_scene_states()
22
-
23
- def set_shared_event(self, event: pygame.Event) -> None:
24
- """
25
- Add an event that will be propagated to all active scenes, not just the one on top.
26
- """
27
- self.shared_events.add(event)
28
-
29
- def print_status(self):
30
- """
31
- Print detailed information about the current state of the scenes and shared variables.
32
- """
33
-
34
- def format_scene_info(scene:bf.Scene):
35
- status = 'Active' if scene.active else 'Inactive'
36
- visibility = 'Visible' if scene.visible else 'Invisible'
37
- return f"{scene.name:<30} | {status:<8} | {visibility:<10} | Index={scene.scene_index}"
38
-
39
- def format_shared_variable(name, value):
40
- return f"[{name}] = {value}"
41
-
42
- print("\n" + "=" * 50)
43
- print(" SCENE STATUS".center(50))
44
- print("=" * 50)
45
-
46
- # Print scene information
47
- if self.scenes:
48
- header = f"{'Scene Name':<30} | {'Status':<8} | {'Visibility':<10} | {'Index':<7}"
49
- print(header)
50
- print("-" * 50)
51
- print("\n".join(format_scene_info(s) for s in self.scenes))
52
- else:
53
- print("No scenes available.")
54
-
55
- # Print debugging mode status
56
- print("\n" + "=" * 50)
57
- print(" DEBUGGING STATUS".center(50))
58
- print("=" * 50)
59
- print(f"[Debugging Mode] = {bf.ResourceManager().get_sharedVar("debug_mode")}")
60
-
61
- # Print shared variables
62
- print("\n" + "=" * 50)
63
- print(" SHARED VARIABLES".center(50))
64
- print("=" * 50)
65
-
66
- if bf.ResourceManager().shared_variables:
67
- for name, value in bf.ResourceManager().shared_variables.items():
68
- print(format_shared_variable(name, value))
69
- else:
70
- print("No shared variables available.")
71
-
72
- print("=" * 50 + "\n")
73
-
74
- def get_current_scene_name(self) -> str:
75
- """get the name of the current scene"""
76
- return self.scenes[0].get_name()
77
-
78
- def get_current_scene(self) -> bf.Scene:
79
- return self.scenes[0]
80
-
81
- def update_scene_states(self):
82
- self.active_scenes = [s for s in reversed(self.scenes) if s.active]
83
- self.visible_scenes = [s for s in reversed(self.scenes) if s.visible]
84
-
85
- def add_scene(self, scene: bf.Scene):
86
- if scene in self.scenes and not self.has_scene(scene.name):
87
- return
88
- scene.set_manager(self)
89
- scene.when_added()
90
- self.scenes.insert(0, scene)
91
-
92
- def remove_scene(self, name: str):
93
- self.scenes = [s for s in self.scenes if s.name != name]
94
-
95
- def has_scene(self, name:str):
96
- return any(name == scene.name for scene in self.scenes)
97
-
98
- def get_scene(self, name:str):
99
- if not self.has_scene(name):
100
- return None
101
- for scene in self.scenes:
102
- if scene.name == name:
103
- return scene
104
-
105
- def get_scene_at(self, index: int) -> bf.Scene | None:
106
- if index < 0 or index >= len(self.scenes):
107
- return None
108
- return self.scenes[index]
109
-
110
- def transition_to_scene(
111
- self,
112
- scene_name: str,
113
- transition: bf.transition.Transition = None,
114
- index: int = 0,
115
- ):
116
- if transition is None:
117
- transition = bf.transition.Fade(0.1)
118
- if not (target_scene := self.get_scene(scene_name)):
119
- print(f"Scene '{scene_name}' does not exist")
120
- return
121
- if not (source_scene := self.get_scene_at(index)):
122
- print(f"No scene exists at index {index}.")
123
- return
124
-
125
- source_surface = bf.const.SCREEN.copy()
126
- dest_surface = bf.const.SCREEN.copy()
127
-
128
- target_scene.draw(dest_surface) # draw at least once to ensure smooth transition
129
- target_scene.set_active(True)
130
- target_scene.set_visible(True)
131
-
132
- target_scene.do_on_enter_early()
133
- source_scene.do_on_exit_early()
134
-
135
- self.current_transition :tuple[str,bf.transition.Transition]=(scene_name,transition,index)
136
- transition.set_source(source_surface)
137
- transition.set_dest(dest_surface)
138
- transition.start()
139
-
140
-
141
-
142
- def set_scene(self, scene_name, index=0, ignore_early: bool = False):
143
- target_scene = self.get_scene(scene_name)
144
- if not target_scene:
145
- print(f"'{scene_name}' does not exist")
146
- return
147
- if len(self.scenes) == 0 or index >= len(self.scenes) or index < 0:
148
- return
149
-
150
- # switch
151
- if not ignore_early:
152
- self.scenes[index].do_on_exit_early()
153
- self.scenes[index].on_exit()
154
- # re-insert scene at index 0
155
- self.scenes.remove(target_scene)
156
- self.scenes.insert(index, target_scene)
157
- _ = [s.set_scene_index(i) for i, s in enumerate(self.scenes)]
158
- if not ignore_early:
159
- self.scenes[index].do_on_enter_early()
160
- target_scene.on_enter()
161
-
162
-
163
-
164
- def cycle_debug_mode(self):
165
- current_index = bf.ResourceManager().get_sharedVar("debug_mode").value
166
- next_index = (current_index + 1) % len(bf.debugMode)
167
- bf.ResourceManager().set_sharedVar("debug_mode", bf.debugMode(next_index))
168
- return bf.debugMode(next_index)
169
-
170
- def process_event(self, event: pygame.Event):
171
-
172
- if event.type in self.shared_events:
173
- [s.process_event(event) for s in self.scenes]
174
- else:
175
- self.scenes[0].process_event(event)
176
-
177
- def update(self, dt: float) -> None:
178
- for scene in self.active_scenes:
179
- scene.update(dt)
180
- if self.current_transition and self.current_transition[1].is_over:
181
- self.set_scene(self.current_transition[0],self.current_transition[2],True)
182
- self.current_transition = None
183
- self.do_update(dt)
184
-
185
- def do_update(self, dt: float):
186
- pass
187
-
188
- def draw(self, surface:pygame.Surface) -> None:
189
- for scene in self.visible_scenes:
190
- scene.draw(surface)
191
- if self.current_transition is not None:
192
- self.current_transition[1].set_source(surface)
193
- tmp = surface.copy()
194
- self.get_scene(self.current_transition[0]).draw(tmp)
195
- self.current_transition[1].set_dest(tmp)
196
- self.current_transition[1].draw(surface)
197
-
1
+ import batFramework as bf
2
+ import pygame
3
+ from typing import Self
4
+
5
+ def swap(lst, index1, index2):
6
+ lst[index1], lst[index2] = lst[index2], lst[index1]
7
+
8
+
9
+ class SceneManager:
10
+ def __init__(self) -> None:
11
+ self.scenes: list[bf.BaseScene] = []
12
+ self.shared_events = {pygame.WINDOWRESIZED}
13
+ self.current_transition : tuple[str,bf.transition.Transition,int] | None= None
14
+
15
+ def init_scenes(self, *initial_scenes:bf.Scene):
16
+ for index, s in enumerate(initial_scenes):
17
+ s.set_scene_index(index)
18
+ for s in reversed(initial_scenes):
19
+ self.add_scene(s)
20
+ self.set_scene(initial_scenes[0].get_name())
21
+ self.update_scene_states()
22
+
23
+ def set_shared_event(self, event: pygame.Event) -> None:
24
+ """
25
+ Add an event that will be propagated to all active scenes, not just the one on top.
26
+ """
27
+ self.shared_events.add(event)
28
+
29
+ def print_status(self):
30
+ """
31
+ Print detailed information about the current state of the scenes and shared variables.
32
+ """
33
+
34
+ def format_scene_info(scene:bf.Scene):
35
+ status = 'Active' if scene.active else 'Inactive'
36
+ visibility = 'Visible' if scene.visible else 'Invisible'
37
+ return f"{scene.name:<30} | {status:<8} | {visibility:<10} | Index={scene.scene_index}"
38
+
39
+ def format_shared_variable(name, value):
40
+ return f"[{name}] = {value}"
41
+
42
+ print("\n" + "=" * 50)
43
+ print(" SCENE STATUS".center(50))
44
+ print("=" * 50)
45
+
46
+ # Print scene information
47
+ if self.scenes:
48
+ header = f"{'Scene Name':<30} | {'Status':<8} | {'Visibility':<10} | {'Index':<7}"
49
+ print(header)
50
+ print("-" * 50)
51
+ print("\n".join(format_scene_info(s) for s in self.scenes))
52
+ else:
53
+ print("No scenes available.")
54
+
55
+ # Print debugging mode status
56
+ print("\n" + "=" * 50)
57
+ print(" DEBUGGING STATUS".center(50))
58
+ print("=" * 50)
59
+ print(f"[Debugging Mode] = {bf.ResourceManager().get_sharedVar('debug_mode')}")
60
+
61
+ # Print shared variables
62
+ print("\n" + "=" * 50)
63
+ print(" SHARED VARIABLES".center(50))
64
+ print("=" * 50)
65
+
66
+ if bf.ResourceManager().shared_variables:
67
+ for name, value in bf.ResourceManager().shared_variables.items():
68
+ print(format_shared_variable(name, value))
69
+ else:
70
+ print("No shared variables available.")
71
+
72
+ print("=" * 50 + "\n")
73
+
74
+ def get_current_scene_name(self) -> str:
75
+ """get the name of the current scene"""
76
+ return self.scenes[0].get_name()
77
+
78
+ def get_current_scene(self) -> bf.Scene:
79
+ return self.scenes[0]
80
+
81
+ def update_scene_states(self):
82
+ self.active_scenes = [s for s in reversed(self.scenes) if s.active]
83
+ self.visible_scenes = [s for s in reversed(self.scenes) if s.visible]
84
+
85
+ def add_scene(self, scene: bf.Scene):
86
+ if scene in self.scenes and not self.has_scene(scene.name):
87
+ return
88
+ scene.set_manager(self)
89
+ scene.when_added()
90
+ self.scenes.insert(0, scene)
91
+
92
+ def remove_scene(self, name: str):
93
+ self.scenes = [s for s in self.scenes if s.name != name]
94
+
95
+ def has_scene(self, name:str):
96
+ return any(name == scene.name for scene in self.scenes)
97
+
98
+ def get_scene(self, name:str):
99
+ if not self.has_scene(name):
100
+ return None
101
+ for scene in self.scenes:
102
+ if scene.name == name:
103
+ return scene
104
+
105
+ def get_scene_at(self, index: int) -> bf.Scene | None:
106
+ if index < 0 or index >= len(self.scenes):
107
+ return None
108
+ return self.scenes[index]
109
+
110
+ def transition_to_scene(
111
+ self,
112
+ scene_name: str,
113
+ transition: bf.transition.Transition = None,
114
+ index: int = 0,
115
+ ):
116
+ if transition is None:
117
+ transition = bf.transition.Fade(0.1)
118
+ if not (target_scene := self.get_scene(scene_name)):
119
+ print(f"Scene '{scene_name}' does not exist")
120
+ return
121
+ if not (source_scene := self.get_scene_at(index)):
122
+ print(f"No scene exists at index {index}.")
123
+ return
124
+
125
+ source_surface = bf.const.SCREEN.copy()
126
+ dest_surface = bf.const.SCREEN.copy()
127
+
128
+ target_scene.draw(dest_surface) # draw at least once to ensure smooth transition
129
+ target_scene.set_active(True)
130
+ target_scene.set_visible(True)
131
+
132
+ target_scene.do_on_enter_early()
133
+ source_scene.do_on_exit_early()
134
+
135
+ self.current_transition :tuple[str,bf.transition.Transition]=(scene_name,transition,index)
136
+ transition.set_source(source_surface)
137
+ transition.set_dest(dest_surface)
138
+ transition.start()
139
+
140
+
141
+
142
+ def set_scene(self, scene_name, index=0, ignore_early: bool = False):
143
+ target_scene = self.get_scene(scene_name)
144
+ if not target_scene:
145
+ print(f"'{scene_name}' does not exist")
146
+ return
147
+ if len(self.scenes) == 0 or index >= len(self.scenes) or index < 0:
148
+ return
149
+
150
+ # switch
151
+ if not ignore_early:
152
+ self.scenes[index].do_on_exit_early()
153
+ self.scenes[index].on_exit()
154
+ # re-insert scene at index 0
155
+ self.scenes.remove(target_scene)
156
+ self.scenes.insert(index, target_scene)
157
+ _ = [s.set_scene_index(i) for i, s in enumerate(self.scenes)]
158
+ if not ignore_early:
159
+ self.scenes[index].do_on_enter_early()
160
+ target_scene.on_enter()
161
+
162
+
163
+
164
+ def cycle_debug_mode(self):
165
+ current_index = bf.ResourceManager().get_sharedVar("debug_mode").value
166
+ next_index = (current_index + 1) % len(bf.debugMode)
167
+ bf.ResourceManager().set_sharedVar("debug_mode", bf.debugMode(next_index))
168
+ return bf.debugMode(next_index)
169
+
170
+ def set_debug_mode(self,debugMode : bf.debugMode):
171
+ bf.ResourceManager().set_sharedVar("debug_mode", debugMode)
172
+
173
+ def process_event(self, event: pygame.Event):
174
+
175
+ if event.type in self.shared_events:
176
+ [s.process_event(event) for s in self.scenes]
177
+ else:
178
+ self.scenes[0].process_event(event)
179
+
180
+ def update(self, dt: float) -> None:
181
+ for scene in self.active_scenes:
182
+ scene.update(dt)
183
+ if self.current_transition and self.current_transition[1].is_over:
184
+ self.set_scene(self.current_transition[0],self.current_transition[2],True)
185
+ self.current_transition = None
186
+ self.do_update(dt)
187
+
188
+ def do_update(self, dt: float):
189
+ pass
190
+
191
+ def draw(self, surface:pygame.Surface) -> None:
192
+ for scene in self.visible_scenes:
193
+ scene.draw(surface)
194
+ if self.current_transition is not None:
195
+ self.current_transition[1].set_source(surface)
196
+ tmp = surface.copy()
197
+ self.get_scene(self.current_transition[0]).draw(tmp)
198
+ self.current_transition[1].set_dest(tmp)
199
+ self.current_transition[1].draw(surface)
200
+
@@ -1,115 +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__(size, data, convert_alpha)
20
- if self.original_surface:
21
- self.original_width, self.original_height = self.original_surface.get_size()
22
-
23
-
24
- def get_debug_outlines(self):
25
- yield from super().get_debug_outlines()
26
- for r in self._get_mosaic_rect_list():
27
- yield r.move(*self.rect.topleft)
28
-
29
-
30
- def set_autoscroll(self, x: float, y: float) -> Self:
31
- self.auto_scroll.update(x, y)
32
- return self
33
-
34
- def set_scroll(self, x: float = None, y: float = None) -> Self:
35
- self.scroll_value.update(
36
- x if x else self.scroll_value.x, y if y else self.scroll_value.y
37
- )
38
- return self
39
-
40
- def scroll(self, x: float, y: float) -> Self:
41
- self.scroll_value += x, y
42
- return self
43
-
44
- def update(self, dt: float) -> None:
45
- if self.auto_scroll:
46
- self.scroll(*self.auto_scroll * dt)
47
- original_width, original_height = self.original_surface.get_size()
48
-
49
- # Use integer values for the starting points, converted from floating point scroll values
50
-
51
- if self.scroll_value.x > self.original_width:
52
- self.scroll_value.x -= self.original_width
53
- if self.scroll_value.y > self.original_height:
54
- self.scroll_value.y -= self.original_height
55
-
56
- super().update(dt)
57
-
58
- def set_size(self, size: tuple[int | None, int | None]) -> Self:
59
- size = list(size)
60
- if size[0] is None:
61
- size[0] = self.rect.w
62
- if size[1] is None:
63
- size[1] = self.rect.h
64
-
65
- self.surface = pygame.Surface(size).convert_alpha()
66
- self.rect = self.surface.get_frect(topleft=self.rect.topleft)
67
- return self
68
-
69
- def _get_mosaic_rect_list(self,camera:bf.Camera=None) -> Iterator[pygame.Rect]:
70
- # Use integer values for the starting points, converted from floating point scroll values
71
- start_x = int(self.scroll_value.x % self.original_width)
72
- start_y = int(self.scroll_value.y % self.original_height)
73
-
74
- # Adjust start_x and start_y to begin tiling off-screen to the top-left, covering all visible area
75
- if start_x != 0:
76
- start_x -= self.original_width
77
- if start_y != 0:
78
- start_y -= self.original_height
79
-
80
- # Set the region in which to tile
81
- end_x = self.rect.w
82
- end_y = self.rect.h
83
-
84
- # Starting y_position for the inner loop
85
- y_position = start_y
86
-
87
- # 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)
88
- # Generate all necessary rectangles
89
- x = start_x
90
- while x < end_x:
91
- y = y_position
92
- while y < end_y:
93
- r = pygame.Rect(x, y, self.original_width, self.original_height)
94
-
95
- if camera and camera.rect.colliderect((x+camera.rect.x,y+camera.rect.y,self.original_width,self.original_height)):
96
- yield r
97
- else:
98
- yield r
99
- y += self.original_height
100
- x += self.original_width
101
- return self
102
-
103
- def draw(self, camera: bf.Camera) -> None:
104
- if not (
105
- self.visible
106
- and (self.surface is not None)
107
- and camera.rect.colliderect(self.rect)
108
- ):
109
- return
110
- # self.surface.fill((0, 0, 0, 0))
111
- camera.surface.fblits(
112
- [(self.original_surface, r.move(self.rect.x-camera.rect.x,self.rect.y-camera.rect.y)) for r in self._get_mosaic_rect_list(camera)]
113
- )
114
- # camera.surface.blit(self.surface, camera.world_to_screen(self.rect))
115
- return
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__(size, data, convert_alpha)
20
+ if self.original_surface:
21
+ self.original_width, self.original_height = self.original_surface.get_size()
22
+
23
+
24
+ def get_debug_outlines(self):
25
+ yield from super().get_debug_outlines()
26
+ for r in self._get_mosaic_rect_list():
27
+ yield r.move(*self.rect.topleft)
28
+
29
+
30
+ def set_autoscroll(self, x: float, y: float) -> Self:
31
+ self.auto_scroll.update(x, y)
32
+ return self
33
+
34
+ def set_scroll(self, x: float = None, y: float = None) -> Self:
35
+ self.scroll_value.update(
36
+ x if x else self.scroll_value.x, y if y else self.scroll_value.y
37
+ )
38
+ return self
39
+
40
+ def scroll(self, x: float, y: float) -> Self:
41
+ self.scroll_value += x, y
42
+ return self
43
+
44
+ def update(self, dt: float) -> None:
45
+ if self.auto_scroll:
46
+ self.scroll(*self.auto_scroll * dt)
47
+ original_width, original_height = self.original_surface.get_size()
48
+
49
+ # Use integer values for the starting points, converted from floating point scroll values
50
+
51
+ if self.scroll_value.x > self.original_width:
52
+ self.scroll_value.x -= self.original_width
53
+ if self.scroll_value.y > self.original_height:
54
+ self.scroll_value.y -= self.original_height
55
+
56
+ super().update(dt)
57
+
58
+ def set_size(self, size: tuple[int | None, int | None]) -> Self:
59
+ size = list(size)
60
+ if size[0] is None:
61
+ size[0] = self.rect.w
62
+ if size[1] is None:
63
+ size[1] = self.rect.h
64
+
65
+ self.surface = pygame.Surface(size).convert_alpha()
66
+ self.rect = self.surface.get_frect(topleft=self.rect.topleft)
67
+ return self
68
+
69
+ def _get_mosaic_rect_list(self,camera:bf.Camera=None) -> Iterator[pygame.Rect]:
70
+ # Use integer values for the starting points, converted from floating point scroll values
71
+ start_x = int(self.scroll_value.x % self.original_width)
72
+ start_y = int(self.scroll_value.y % self.original_height)
73
+
74
+ # Adjust start_x and start_y to begin tiling off-screen to the top-left, covering all visible area
75
+ if start_x != 0:
76
+ start_x -= self.original_width
77
+ if start_y != 0:
78
+ start_y -= self.original_height
79
+
80
+ # Set the region in which to tile
81
+ end_x = self.rect.w
82
+ end_y = self.rect.h
83
+
84
+ # Starting y_position for the inner loop
85
+ y_position = start_y
86
+
87
+ # 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)
88
+ # Generate all necessary rectangles
89
+ x = start_x
90
+ while x < end_x:
91
+ y = y_position
92
+ while y < end_y:
93
+ r = pygame.Rect(x, y, self.original_width, self.original_height)
94
+
95
+ if camera and camera.rect.colliderect((x+camera.rect.x,y+camera.rect.y,self.original_width,self.original_height)):
96
+ yield r
97
+ else:
98
+ yield r
99
+ y += self.original_height
100
+ x += self.original_width
101
+ return self
102
+
103
+ def draw(self, camera: bf.Camera) -> None:
104
+ if not (
105
+ self.visible
106
+ and (self.surface is not None)
107
+ and camera.rect.colliderect(self.rect)
108
+ ):
109
+ return
110
+ # self.surface.fill((0, 0, 0, 0))
111
+ camera.surface.fblits(
112
+ [(self.original_surface, r.move(self.rect.x-camera.rect.x,self.rect.y-camera.rect.y)) for r in self._get_mosaic_rect_list(camera)]
113
+ )
114
+ # camera.surface.blit(self.surface, camera.world_to_screen(self.rect))
115
+ return