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
batFramework/camera.py CHANGED
@@ -1,317 +1,245 @@
1
- import pygame
2
- from pygame.math import Vector2
3
- import batFramework as bf
4
- from typing import Self
5
- import math
6
-
7
-
8
- class Camera:
9
- def __init__(
10
- self, flags=0, size: tuple[int, int] | None = None, convert_alpha: bool = False
11
- ) -> None:
12
- """
13
- Initialize the Camera object.
14
-
15
- Args:
16
- flags (int): Flags for camera initialization.
17
- size (tuple): Optional size for camera (defaults to window size)
18
- convert_alpha (bool): Convert surface to support alpha values
19
- """
20
- self.cached_surfaces: dict[tuple[int, int], pygame.Surface] = {}
21
-
22
- self.target_size = size if size else bf.const.RESOLUTION
23
- self.should_convert_alpha: bool = convert_alpha
24
- self.rect = pygame.FRect(0, 0, *self.target_size)
25
- self.vector_center = Vector2(0, 0)
26
-
27
- self.surface: pygame.Surface = pygame.Surface((0, 0)) # tmp dummy
28
- self.flags: int = flags | (pygame.SRCALPHA if convert_alpha else 0)
29
- self.blit_special_flags: int = pygame.BLEND_ALPHA_SDL2
30
-
31
- self._clear_color: pygame.Color = pygame.Color(0, 0, 0, 0)
32
- if not convert_alpha:
33
- self._clear_color = pygame.Color(0, 0, 0)
34
-
35
- self.follow_point_func = None
36
- self.damping = float("inf")
37
- self.dead_zone_radius = 10
38
-
39
- self.zoom_factor = 1
40
- self.max_zoom = 2
41
- self.min_zoom = 0.1
42
- self.zoom(1)
43
-
44
- def get_mouse_pos(self)->tuple[float,float]:
45
- return self.screen_to_world(pygame.mouse.get_pos())
46
-
47
- def set_clear_color(self, color: pygame.Color | tuple | str) -> Self:
48
- """
49
- Set the clear color for the camera surface.
50
-
51
- Args:
52
- color (pygame.Color | tuple): Color to set as the clear color.
53
-
54
- Returns:
55
- Camera: Returns the Camera object.
56
- """
57
- self._clear_color = color
58
- return self
59
-
60
- def set_max_zoom(self, value: float) -> Self:
61
- """
62
- Set the maximum zoom value for the camera.
63
-
64
- Args:
65
- value (float): Maximum zoom value.
66
-
67
- Returns:
68
- Camera: Returns the Camera object.
69
- """
70
- self.max_zoom = value
71
- return self
72
-
73
- def set_min_zoom(self, value: float) -> Self:
74
- """
75
- Set the minimum zoom value for the camera.
76
-
77
- Args:
78
- value (float): Minimum zoom value.
79
-
80
- Returns:
81
- Camera: Returns the Camera object.
82
- """
83
- self.min_zoom = value
84
- return self
85
-
86
- def clear(self) -> None:
87
- """
88
- Clear the camera surface with the set clear color.
89
- """
90
- if not self._clear_color: return
91
- self.surface.fill(self._clear_color)
92
-
93
- def get_center(self) -> tuple[float, float]:
94
- """
95
- Get the center of the camera's view.
96
-
97
- Returns:
98
- tuple[float,float]: Returns the center coordinates.
99
- """
100
- return self.rect.center
101
-
102
- def get_position(self) -> tuple[float, float]:
103
- """
104
- Get the center of the camera's view.
105
-
106
- Returns:
107
- tuple[float,float]: Returns the center coordinates.
108
- """
109
- return self.rect.topleft
110
-
111
- def move_by(self, x: float | int, y: float | int) -> Self:
112
- """
113
- Moves the camera rect by the given coordinates.
114
-
115
- Args:
116
- x: X-coordinate to move.
117
- y: Y-coordinate to move.
118
-
119
- Returns:
120
- Camera: Returns the Camera object.
121
- """
122
- self.rect.move_ip(x, y)
123
- return self
124
-
125
- def set_position(self, x, y) -> Self:
126
- """
127
- Set the camera rect top-left position.
128
-
129
- Args:
130
- x: X-coordinate to set.
131
- y: Y-coordinate to set.
132
-
133
- Returns:
134
- Camera: Returns the Camera object.
135
- """
136
- self.rect.topleft = (x, y)
137
-
138
- return self
139
-
140
- def set_center(self, x, y) -> Self:
141
- """
142
- Set the camera rect center position.
143
-
144
- Args:
145
- x: X-coordinate for the center.
146
- y: Y-coordinate for the center.
147
-
148
- Returns:
149
- Camera: Returns the Camera object.
150
- """
151
- self.rect.center = (x, y)
152
- return self
153
-
154
- def set_follow_point_func(self, func) -> Self:
155
- self.follow_point_func = func
156
- return self
157
-
158
- def set_follow_speed(self, speed: float) -> Self:
159
- self.follow_speed = speed
160
- return self
161
-
162
- def set_follow_damping(self, damping: float) -> Self:
163
- self.damping = damping
164
- return self
165
-
166
- def set_dead_zone_radius(self, radius: float) -> Self:
167
- self.dead_zone_radius = radius
168
- return self
169
-
170
- def zoom_by(self, amount: float) -> Self:
171
- """
172
- Zooms the camera by the given amount.
173
-
174
- Args:
175
- amount (float): Amount to zoom.
176
-
177
- Returns:
178
- Camera: Returns the Camera object.
179
- """
180
- self.zoom(max(self.min_zoom, min(self.max_zoom, self.zoom_factor + amount)))
181
- return self
182
-
183
- def zoom(self, factor: float) -> Self:
184
- """
185
- Zooms the camera to the given factor.
186
-
187
- Args:
188
- factor: Factor to set for zooming.
189
-
190
- Returns:
191
- Camera: Returns the Camera object.
192
- """
193
- if factor < self.min_zoom or factor > self.max_zoom:
194
- print(
195
- f"Zoom value {factor} is outside the zoom range : [{self.min_zoom},{self.max_zoom}]"
196
- )
197
- return self
198
-
199
- factor = round(factor, 2)
200
- self.zoom_factor = factor
201
- new_res = tuple(round((i / factor) / 2) * 2 for i in self.target_size)
202
- self.surface = self._get_cached_surface(new_res)
203
- self.rect = self.surface.get_frect(center=self.rect.center)
204
- self.clear()
205
- return self
206
-
207
- def _free_cache(self):
208
- self.cached_surfaces = {}
209
-
210
- def _get_cached_surface(self, new_size: tuple[int, int]):
211
- surface = self.cached_surfaces.get(new_size, None)
212
- if surface is None:
213
- surface = pygame.Surface(new_size, flags=self.flags)
214
- if self.flags & pygame.SRCALPHA:
215
- surface = surface.convert_alpha()
216
- self.cached_surfaces[new_size] = surface
217
-
218
- return surface
219
-
220
- def set_size(self, size: tuple[int, int] | None = None) -> Self:
221
- if size is None:
222
- size = bf.const.RESOLUTION
223
- self.target_size = size
224
- self.rect.center = (size[0] / 2, size[1] / 2)
225
- self.zoom(self.zoom_factor)
226
-
227
- return self
228
-
229
- def intersects(self, rect: pygame.Rect | pygame.FRect) -> bool:
230
- """
231
- Check if the camera view intersects with the given rectangle.
232
-
233
- Args:
234
- rect (pygame.Rect | pygame.FRect): Rectangle to check intersection with.
235
-
236
- Returns:
237
- bool: True if intersection occurs, False otherwise.
238
- """
239
- return self.rect.colliderect(rect)
240
-
241
- def world_to_screen(
242
- self, rect: pygame.Rect | pygame.FRect
243
- ) -> pygame.Rect | pygame.FRect:
244
- """
245
- world_to_screen the given rectangle coordinates relative to the camera.
246
-
247
- Args:
248
- rect (pygame.Rect | pygame.FRect): Rectangle to world_to_screen.
249
-
250
- Returns:
251
- pygame.FRect: Transposed rectangle.
252
- """
253
- return pygame.FRect(
254
- rect[0] - self.rect.left, rect[1] - self.rect.top, rect[2], rect[3]
255
- )
256
-
257
- def world_to_screen_point(
258
- self, point: tuple[float, float] | tuple[int, int]
259
- ) -> tuple[float, float]:
260
- """
261
- world_to_screen the given 2D point coordinates relative to the camera.
262
-
263
- Args:
264
- point (tuple[float,float] | tuple[int,int]): Point to world_to_screen.
265
-
266
- Returns:
267
- tuple[float,float] : Transposed point.
268
- """
269
- return point[0] - self.rect.x, point[1] - self.rect.y
270
-
271
- def screen_to_world(
272
- self, point: tuple[float, float] | tuple[int, int]
273
- ) -> tuple[float, float]:
274
- """
275
- Convert screen coordinates to world coordinates based on camera settings.
276
-
277
- Args:
278
- point (tuple[float,float] | tuple[int,int]): Point to screen_to_world.
279
- Returns:
280
- tuple: Converted world coordinates.
281
- """
282
- return (
283
- point[0] / self.zoom_factor + self.rect.x,
284
- point[1] / self.zoom_factor + self.rect.y,
285
- )
286
-
287
- def update(self, dt):
288
- if not self.follow_point_func or not (math.isfinite(dt) and dt > 0):
289
- return
290
-
291
- target = Vector2(self.follow_point_func())
292
- self.vector_center.update(self.rect.center)
293
-
294
- if self.damping == float("inf"):
295
- self.vector_center = target
296
- elif math.isfinite(self.damping) and self.damping > 0:
297
- damping_factor = 1 - math.exp(-self.damping * dt)
298
- if not math.isnan(damping_factor):
299
- self.vector_center += (target - self.vector_center) * damping_factor
300
-
301
- self.rect.center = self.vector_center
302
-
303
- def draw(self, surface: pygame.Surface):
304
- """
305
- Draw the camera view onto the provided surface with proper scaling.
306
-
307
- Args:
308
- surface (pygame.Surface): Surface to draw the camera view onto.
309
- """
310
- if self.zoom_factor == 1:
311
- surface.blit(self.surface, (0, 0), special_flags=self.blit_special_flags)
312
- # surface.blit(self.surface, (0, 0))
313
- return
314
-
315
- # Scale the surface to match the resolution
316
- scaled_surface = pygame.transform.scale(self.surface, self.target_size)
317
- surface.blit(scaled_surface, (0, 0), special_flags=self.blit_special_flags)
1
+ import pygame
2
+ from pygame.math import Vector2
3
+ import batFramework as bf
4
+ from typing import Self
5
+ import math
6
+
7
+
8
+ class Camera:
9
+ def __init__(
10
+ self, flags=0, size: tuple[int, int] | None = None, convert_alpha: bool = False,fullscreen:bool=True
11
+ ) -> None:
12
+ self.cached_surfaces: dict[tuple[int, int], pygame.Surface] = {}
13
+ self.fullscreen : bool = fullscreen # auto fill the screen (i.e react to VIDEORESIZE event)
14
+ self.flags: int = flags | (pygame.SRCALPHA if convert_alpha else 0)
15
+ self.blit_special_flags: int = pygame.BLEND_ALPHA_SDL2
16
+
17
+ size = size if size else bf.const.RESOLUTION
18
+ self.rect = pygame.FRect(0, 0, *size)
19
+ self.transform_target_surface : pygame.Surface = pygame.Surface(self.rect.size,self.flags)
20
+ self.should_convert_alpha: bool = convert_alpha
21
+ self.world_rect = pygame.FRect(0, 0, *self.rect.size)
22
+
23
+ self.vector_center = Vector2(0, 0)
24
+ self.rotation = 0.0 # Rotation in degrees
25
+
26
+ self.surface: pygame.Surface = pygame.Surface((0, 0)) # dynamic : create new at each new zoom value
27
+
28
+ self._clear_color: pygame.typing.ColorLike = pygame.Color(0, 0, 0, 0) if convert_alpha else pygame.Color(0, 0, 0)
29
+
30
+ self.follow_point_func = None
31
+ self.damping = float("inf")
32
+ self.dead_zone_radius = 10
33
+
34
+ self.zoom_factor = 1
35
+ self.max_zoom = 2
36
+ self.min_zoom = 0.1
37
+ self.zoom(1,force=True)
38
+
39
+ def get_mouse_pos(self) -> tuple[float, float]:
40
+ return self.screen_to_world(pygame.mouse.get_pos())
41
+
42
+ def set_clear_color(self, color: pygame.Color | tuple | str) -> Self:
43
+ self._clear_color = color
44
+ return self
45
+
46
+ def set_max_zoom(self, value: float) -> Self:
47
+ self.max_zoom = value
48
+ return self
49
+
50
+ def set_min_zoom(self, value: float) -> Self:
51
+ self.min_zoom = value
52
+ return self
53
+
54
+ def set_rotation(self, angle: float) -> Self:
55
+ """
56
+ Set the camera rotation in degrees.
57
+ """
58
+ self.rotation = angle % 360
59
+ return self
60
+
61
+ def rotate_by(self,angle:float)->Self:
62
+ """
63
+ Increment rotation by given angle in degrees.
64
+ """
65
+ self.rotation+=angle
66
+ self.rotation%=360
67
+ return self
68
+
69
+ def clear(self) -> None:
70
+ if self._clear_color is None:
71
+ return
72
+ self.surface.fill(self._clear_color)
73
+
74
+ def get_center(self) -> tuple[float, float]:
75
+ return self.world_rect.center
76
+
77
+ def get_position(self) -> tuple[float, float]:
78
+ return self.world_rect.topleft
79
+
80
+ def move_by(self, x: float | int, y: float | int) -> Self:
81
+ # self.world_rect.move_ip(x, y)
82
+ self.world_rect.x += x
83
+ self.world_rect.y += y
84
+ return self
85
+
86
+ def set_position(self, x, y) -> Self:
87
+ self.world_rect.topleft = (x, y)
88
+ return self
89
+
90
+ def set_center(self, x, y) -> Self:
91
+ self.world_rect.center = (x, y)
92
+ return self
93
+
94
+ def set_follow_point_func(self, func) -> Self:
95
+ self.follow_point_func = func
96
+ return self
97
+
98
+ def set_follow_speed(self, speed: float) -> Self:
99
+ self.follow_speed = speed
100
+ return self
101
+
102
+ def set_follow_damping(self, damping: float) -> Self:
103
+ self.damping = damping
104
+ return self
105
+
106
+ def set_dead_zone_radius(self, radius: float) -> Self:
107
+ self.dead_zone_radius = radius
108
+ return self
109
+
110
+ def zoom_by(self, amount: float) -> Self:
111
+ return self.zoom(self.zoom_factor + amount)
112
+
113
+ def zoom(self, factor: float,force:bool=False) -> Self:
114
+ clamped = max(self.min_zoom, min(self.max_zoom, round(factor, 2)))
115
+ if clamped == self.zoom_factor and not force:
116
+ return self
117
+
118
+ self.zoom_factor = clamped
119
+ new_res = tuple([round((i / clamped) / 2) * 2 for i in self.rect.size])
120
+
121
+ if self.surface.get_size() != new_res:
122
+ self.surface = self._get_cached_surface((new_res[0],new_res[1]))
123
+
124
+ self.world_rect = self.surface.get_frect(center=self.world_rect.center)
125
+ self.clear()
126
+ return self
127
+
128
+ def _free_cache(self):
129
+ self.cached_surfaces.clear()
130
+
131
+ def _get_cached_surface(self, new_size: tuple[int, int]):
132
+ surface = self.cached_surfaces.get(new_size)
133
+ if surface is None:
134
+ surface = pygame.Surface(new_size, flags=self.flags)
135
+ # if self.flags & pygame.SRCALPHA:
136
+ # surface = surface.convert_alpha()
137
+ self.cached_surfaces[new_size] = surface
138
+ return surface
139
+
140
+ def set_size(self, size: tuple[int, int] | None = None) -> Self:
141
+ if size is None:
142
+ size = bf.const.RESOLUTION
143
+ center = self.rect.center
144
+ self.rect.size = size
145
+ self.rect.center = center
146
+ self.transform_target_surface = pygame.Surface(self.rect.size,self.flags)
147
+ self.world_rect.center = (size[0] / 2, size[1] / 2)
148
+ self.zoom(self.zoom_factor)
149
+ return self
150
+
151
+ def intersects(self, rect: pygame.Rect | pygame.FRect) -> bool:
152
+ return self.world_rect.colliderect(rect)
153
+
154
+ def world_to_screen(self, rect: pygame.Rect | pygame.FRect) -> pygame.FRect:
155
+ return pygame.FRect(
156
+ (rect[0] - self.world_rect.left), (rect[1] - self.world_rect.top), rect[2], rect[3]
157
+ )
158
+
159
+ def world_to_screen_scaled(self, rect: pygame.Rect | pygame.FRect) -> pygame.FRect:
160
+ screen_rect = self.world_to_screen(rect)
161
+ return pygame.FRect(
162
+ screen_rect.x * self.zoom_factor,
163
+ screen_rect.y * self.zoom_factor,
164
+ screen_rect.w * self.zoom_factor,
165
+ screen_rect.h * self.zoom_factor,
166
+ )
167
+
168
+ def world_to_screen_point(self, point: tuple[float, float] | tuple[int, int]) -> tuple[float, float]:
169
+ return (
170
+ (point[0] - self.world_rect.x),
171
+ (point[1] - self.world_rect.y),
172
+ )
173
+
174
+ def world_to_screen_point_scaled(self, point: tuple[float, float] | tuple[int, int]) -> tuple[float, float]:
175
+ return (
176
+ (point[0] - self.world_rect.x) * self.zoom_factor,
177
+ (point[1] - self.world_rect.y) * self.zoom_factor,
178
+ )
179
+
180
+
181
+ def screen_to_world(self, point: tuple[float, float] | tuple[int, int]) -> tuple[float, float]:
182
+ """
183
+ roates, scales and translates point to world coordinates
184
+
185
+ """
186
+ # Center of the screen (zoomed+rotated surface)
187
+ cx, cy = self.rect.w / 2, self.rect.h / 2
188
+
189
+ # Offset from center
190
+ dx, dy = point[0] - cx, point[1] - cy
191
+
192
+ # rotate that offset
193
+ if self.rotation != 0:
194
+ angle_rad = math.radians(self.rotation)
195
+ cos_a = math.cos(angle_rad)
196
+ sin_a = math.sin(angle_rad)
197
+ dx, dy = cos_a * dx - sin_a * dy, sin_a * dx + cos_a * dy
198
+
199
+ # Un-zoom and add camera position
200
+ wx = (dx + cx) / self.zoom_factor + self.world_rect.x
201
+ wy = (dy + cy) / self.zoom_factor + self.world_rect.y
202
+ return wx, wy
203
+
204
+
205
+ def update(self, dt: float):
206
+ if not self.follow_point_func or not (math.isfinite(dt) and dt > 0):
207
+ return
208
+
209
+ target = Vector2(self.follow_point_func())
210
+ self.vector_center.xy.update(self.world_rect.center)
211
+
212
+ if self.damping == float("inf"):
213
+ self.vector_center.xy = target.xy
214
+ elif math.isfinite(self.damping) and self.damping > 0:
215
+ damping_factor = 1 - math.exp(-self.damping * dt)
216
+ if not math.isnan(damping_factor):
217
+ diff = target - self.vector_center
218
+ self.vector_center += diff * damping_factor
219
+
220
+ self.world_rect.center = self.vector_center
221
+
222
+
223
+ def draw(self, surface: pygame.Surface):
224
+ """
225
+ Draw the camera view onto the provided surface with proper scaling and rotation.
226
+
227
+ Args:
228
+ surface (pygame.Surface): Surface to draw the camera view onto.
229
+ """
230
+ # Scale the camera surface to the target size
231
+ if self.zoom_factor == 1 and self.rotation == 0:
232
+ surface.blit(self.surface, (0, 0), special_flags=self.blit_special_flags)
233
+ return
234
+
235
+ pygame.transform.scale(self.surface, self.rect.size, self.transform_target_surface)
236
+
237
+ result_surface = self.transform_target_surface
238
+
239
+ if self.rotation != 0:
240
+ # Rotate around the center of the target surface
241
+ rotated_surface = pygame.transform.rotate(result_surface, self.rotation)
242
+ rect = rotated_surface.get_rect(center=(self.rect.w // 2, self.rect.h // 2))
243
+ surface.blit(rotated_surface, rect.topleft, special_flags=self.blit_special_flags)
244
+ else:
245
+ surface.blit(result_surface, (0, 0), special_flags=self.blit_special_flags)