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/manager.py CHANGED
@@ -1,134 +1,131 @@
1
- import batFramework as bf
2
- import pygame
3
- import asyncio
4
-
5
- class Manager(bf.SceneManager):
6
- def __init__(self, *initial_scenes) -> None:
7
- super().__init__()
8
- self.debug_mode: bf.enums.debugMode = bf.debugMode.HIDDEN
9
- self.screen: pygame.Surface | None = bf.const.SCREEN
10
- self.timeManager = bf.TimeManager()
11
- self.cutsceneManager = bf.CutsceneManager()
12
- self.cutsceneManager.set_manager(self)
13
- self.clock: pygame.Clock = pygame.Clock()
14
- self.is_async_running : bool = False
15
- self.running = False
16
- pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
17
- bf.ResourceManager().set_sharedVar("clock", self.clock)
18
- bf.ResourceManager().set_sharedVar("debug_mode", self.debug_mode)
19
-
20
- self.do_pre_init()
21
- if initial_scenes:
22
- self.init_scenes(*initial_scenes)
23
- self.do_init()
24
-
25
- @staticmethod
26
- def set_icon(path: str) -> None:
27
- surf = pygame.image.load(bf.ResourceManager().get_path(path)).convert_alpha()
28
- pygame.display.set_icon(surf)
29
-
30
- def print_status(self):
31
- """
32
- Print detailed information about the current state of the scenes, shared variables,
33
- and additional timers managed by the subclass.
34
- """
35
- # Call the parent class's print_status method to include its information
36
- super().print_status()
37
-
38
- # Add the timers information in a cohesive manner
39
- print("\n" + "=" * 50)
40
- print(" TIMERS".center(50))
41
- print("=" * 50)
42
-
43
- # Print the timers information
44
- print(self.timeManager)
45
-
46
- # End with a visual separator
47
- print("=" * 50 + "\n")
48
-
49
-
50
- def get_fps(self) -> float:
51
- return self.clock.get_fps()
52
-
53
- def do_init(self) -> None:
54
- pass
55
-
56
- def do_pre_init(self) -> None:
57
- pass
58
-
59
- def stop(self) -> None:
60
- self.running = False
61
-
62
- def process_event(self, event: pygame.Event):
63
- event.consumed = False
64
- keys = pygame.key.get_pressed()
65
- if (
66
- bf.const.ALLOW_DEBUG and
67
- keys[pygame.K_LCTRL]
68
- and keys[pygame.K_LSHIFT]
69
- and event.type == pygame.KEYDOWN
70
- ):
71
- if event.key == pygame.K_d:
72
- self.cycle_debug_mode()
73
- return
74
- if event.key == pygame.K_p:
75
- self.print_status()
76
- return
77
- self.cutsceneManager.process_event(event)
78
- if event.type == pygame.VIDEORESIZE and not (bf.const.FLAGS & pygame.SCALED):
79
- bf.const.set_resolution((event.w, event.h))
80
-
81
- if event.consumed: return
82
-
83
- super().process_event(event)
84
- if not event.consumed:
85
- if event.type == pygame.QUIT:
86
- self.running = False
87
-
88
- def update(self, dt: float) -> None:
89
- self.timeManager.update(dt)
90
- self.cutsceneManager.update(dt)
91
- super().update(dt)
92
-
93
-
94
- async def run_async(self):
95
- if len(self.scenes) == 0:
96
- raise Exception("Manager can't start without scenes")
97
- if self.running:
98
- raise Exception("Error : Already running")
99
- self.is_async_running = True
100
- self.running = True
101
- dt: float = 0
102
- while self.running:
103
- for event in pygame.event.get():
104
- self.process_event(event)
105
- # update
106
- self.update(dt)
107
- # render
108
- self.draw(self.screen)
109
- pygame.display.flip()
110
- dt = self.clock.tick(bf.const.FPS) / 1000
111
- dt = min(dt, 0.02) # dirty fix for dt being too high when window not focused for a long time
112
- await asyncio.sleep(0)
113
- pygame.quit()
114
-
115
-
116
- def run(self) -> None:
117
- if len(self.scenes) == 0:
118
- raise Exception("Manager can't start without scenes")
119
- if self.running:
120
- raise Exception("Error : Already running")
121
- return
122
- self.running = True
123
- dt: float = 0
124
- while self.running:
125
- for event in pygame.event.get():
126
- self.process_event(event)
127
- # update
128
- self.update(dt)
129
- # render
130
- self.draw(self.screen)
131
- pygame.display.flip()
132
- dt = self.clock.tick(bf.const.FPS) / 1000
133
- dt = min(dt, 0.02) # dirty fix for dt being too high when window not focused for a long time
134
- pygame.quit()
1
+ import batFramework as bf
2
+ from batFramework import const
3
+ import pygame
4
+ import asyncio
5
+
6
+
7
+ class Manager(bf.SceneManager):
8
+ def __init__(self, *initial_scenes) -> None:
9
+ super().__init__()
10
+ self.debug_mode: bf.enums.debugMode = bf.debugMode.HIDDEN
11
+ self.screen: pygame.Surface | None = bf.const.SCREEN
12
+ self.timeManager = bf.TimeManager()
13
+ self.cutsceneManager = bf.CutsceneManager()
14
+ self.cutsceneManager.set_manager(self)
15
+ self.clock: pygame.Clock = pygame.Clock()
16
+ self.is_async_running : bool = False
17
+ self.running = False
18
+ pygame.mouse.set_cursor(bf.const.DEFAULT_CURSOR)
19
+ bf.ResourceManager().set_sharedVar("clock", self.clock)
20
+ bf.ResourceManager().set_sharedVar("debug_mode", self.debug_mode)
21
+
22
+ self.do_pre_init()
23
+ if initial_scenes:
24
+ self.init_scenes(*initial_scenes)
25
+ self.do_init()
26
+
27
+ @staticmethod
28
+ def set_icon(path: str) -> None:
29
+ surf = pygame.image.load(bf.ResourceManager().get_path(path)).convert_alpha()
30
+ pygame.display.set_icon(surf)
31
+
32
+ def print_status(self):
33
+ """
34
+ Print detailed information about the current state of the scenes, shared variables,
35
+ and additional timers managed by the subclass.
36
+ """
37
+ # Call the parent class's print_status method to include its information
38
+ super().print_status()
39
+ # Add the timers information in a cohesive manner
40
+ print("\n" + "=" * 50)
41
+ print(" TIMERS".center(50))
42
+ print("=" * 50)
43
+ # Print the timers information
44
+ print(self.timeManager)
45
+ # End with a visual separator
46
+ print("=" * 50 + "\n")
47
+
48
+
49
+ def get_fps(self) -> float:
50
+ return self.clock.get_fps()
51
+
52
+ def do_init(self) -> None:
53
+ pass
54
+
55
+ def do_pre_init(self) -> None:
56
+ pass
57
+
58
+ def stop(self) -> None:
59
+ self.running = False
60
+
61
+ def process_event(self, event: pygame.Event):
62
+ event.consumed = False
63
+ keys = pygame.key.get_pressed()
64
+ if (
65
+ bf.const.ALLOW_DEBUG and
66
+ keys[pygame.K_LCTRL]
67
+ and keys[pygame.K_LSHIFT]
68
+ and event.type == pygame.KEYDOWN
69
+ ):
70
+ if event.key == pygame.K_d:
71
+ self.cycle_debug_mode()
72
+ return
73
+ if event.key == pygame.K_p:
74
+ self.print_status()
75
+ return
76
+ self.cutsceneManager.process_event(event)
77
+ if event.type == pygame.VIDEORESIZE and not (bf.const.FLAGS & pygame.SCALED):
78
+ bf.const.set_resolution((event.w, event.h))
79
+
80
+ if event.consumed: return
81
+
82
+ super().process_event(event)
83
+ if not event.consumed:
84
+ if event.type == pygame.QUIT:
85
+ self.running = False
86
+
87
+ def update(self, dt: float) -> None:
88
+ self.timeManager.update(dt)
89
+ self.cutsceneManager.update(dt)
90
+ super().update(dt)
91
+
92
+
93
+ async def run_async(self):
94
+ if len(self.scenes) == 0:
95
+ raise Exception("Manager can't start without scenes")
96
+ if self.running:
97
+ raise Exception("Error : Already running")
98
+ self.is_async_running = True
99
+ self.running = True
100
+ dt: float = 0
101
+ while self.running:
102
+ for event in pygame.event.get():
103
+ self.process_event(event)
104
+ # update
105
+ self.update(dt)
106
+ # render
107
+ self.draw(self.screen)
108
+ pygame.display.flip()
109
+ dt = self.clock.tick(bf.const.FPS) / 1000
110
+ dt = min(dt, 0.02) # dirty fix for dt being too high when window not focused for a long time
111
+ await asyncio.sleep(0)
112
+ pygame.quit()
113
+
114
+ def run(self) -> None:
115
+ if len(self.scenes) == 0:
116
+ raise Exception("Manager can't start without scenes")
117
+ if self.running:
118
+ raise Exception("Error : Already running")
119
+ self.running = True
120
+ dt: float = 0
121
+ while self.running:
122
+ for event in pygame.event.get():
123
+ self.process_event(event)
124
+ # update
125
+ self.update(dt)
126
+ # render
127
+ self.draw(self.screen)
128
+ pygame.display.flip()
129
+ dt = self.clock.tick(bf.const.FPS) / 1000
130
+ dt = min(dt, 0.02) # fix for dt being too high when window not focused for a long time
131
+ pygame.quit()
batFramework/particle.py CHANGED
@@ -1,118 +1,118 @@
1
- import batFramework as bf
2
- import pygame
3
- from pygame.math import Vector2
4
-
5
-
6
- class Particle:
7
- def __init__(self, *args, **kwargs):
8
- self.dead = False
9
- self.surface = None
10
- self.generator = None
11
-
12
- def do_when_added(self):
13
- pass
14
-
15
- def update(self, dt):
16
- pass
17
-
18
- def kill(self):
19
- self.dead = True
20
-
21
- def update_surface(self):
22
- pass
23
-
24
-
25
- class TimedParticle(Particle):
26
- def __init__(self, duration):
27
- super().__init__()
28
- self.duration = duration
29
-
30
- def do_when_added(self):
31
- if self.generator and self.generator.parent_scene:
32
- self.timer = bf.SceneTimer(
33
- self.duration, end_callback=self.kill,
34
- scene_name=self.generator.parent_scene.name).start()
35
- else:
36
- self.timer = bf.Timer(self.duration, end_callback=self.kill).start()
37
-
38
-
39
- class BasicParticle(TimedParticle):
40
- def __init__(
41
- self,
42
- start_pos: tuple[float, float],
43
- start_vel: tuple[float, float],
44
- duration=1,
45
- color=None,
46
- size: tuple[int, int] = (4, 4),
47
- *args,
48
- **kwargs,
49
- ):
50
- super().__init__(duration)
51
- self.rect = pygame.FRect(0,0, *size)
52
- self.rect.center = start_pos
53
- self.surface = pygame.Surface(size)
54
- self.velocity = Vector2(start_vel)
55
- if color:
56
- self.surface.fill(color)
57
- self.start()
58
-
59
- def start(self):
60
- pass
61
-
62
- def update(self, dt):
63
- super().update(dt)
64
- self.rect.center += self.velocity * dt
65
- self.update_surface()
66
-
67
- def update_surface(self):
68
- self.surface.set_alpha(255 - int(self.timer.get_progression() * 255))
69
-
70
-
71
- class DirectionalParticle(BasicParticle):
72
- def start(self):
73
- self.original_surface = self.surface.copy()
74
-
75
- def update_surface(self):
76
- angle = self.velocity.angle_to(Vector2(1, 0))
77
- self.surface = pygame.transform.rotate(self.original_surface, angle)
78
- super().update_surface()
79
-
80
-
81
- class ParticleGenerator(bf.Drawable):
82
- def __init__(self) -> None:
83
- super().__init__((0, 0))
84
- self.particles: list[Particle] = []
85
- self.count = 0
86
-
87
- def get_debug_outlines(self):
88
- return
89
- for particle in self.particles:
90
- yield (
91
- particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
92
- "blue",
93
- )
94
- yield (self.rect, "cyan")
95
-
96
- def add_particle(self, particle:Particle):
97
- particle.generator = self
98
- particle.do_when_added()
99
- self.particles.append(particle)
100
- self.count += 1
101
-
102
- def clear(self):
103
- self.particles = []
104
- self.count = 0
105
- def update(self, dt: float):
106
- particles_to_remove = []
107
- for particle in self.particles:
108
- particle.update(dt)
109
- if particle.dead:
110
- particles_to_remove.append(particle)
111
- for p in particles_to_remove:
112
- self.particles.remove(p)
113
- self.count -= 1
114
-
115
- def draw(self, camera) -> None:
116
- camera.surface.fblits(
117
- [(p.surface, camera.world_to_screen(p.rect)) for p in self.particles]
118
- )
1
+ import batFramework as bf
2
+ import pygame
3
+ from pygame.math import Vector2
4
+
5
+
6
+ class Particle:
7
+ def __init__(self, *args, **kwargs):
8
+ self.dead = False
9
+ self.surface = None
10
+ self.generator = None
11
+
12
+ def do_when_added(self):
13
+ pass
14
+
15
+ def update(self, dt):
16
+ pass
17
+
18
+ def kill(self):
19
+ self.dead = True
20
+
21
+ def update_surface(self):
22
+ pass
23
+
24
+
25
+ class TimedParticle(Particle):
26
+ def __init__(self, duration):
27
+ super().__init__()
28
+ self.duration = duration
29
+
30
+ def do_when_added(self):
31
+ if self.generator and self.generator.parent_scene:
32
+ self.timer = bf.SceneTimer(
33
+ self.duration, end_callback=self.kill,
34
+ scene_name=self.generator.parent_scene.name).start()
35
+ else:
36
+ self.timer = bf.Timer(self.duration, end_callback=self.kill).start()
37
+
38
+
39
+ class BasicParticle(TimedParticle):
40
+ def __init__(
41
+ self,
42
+ start_pos: tuple[float, float],
43
+ start_vel: tuple[float, float],
44
+ duration=1,
45
+ color=None,
46
+ size: tuple[int, int] = (4, 4),
47
+ *args,
48
+ **kwargs,
49
+ ):
50
+ super().__init__(duration)
51
+ self.rect = pygame.FRect(0,0, *size)
52
+ self.rect.center = start_pos
53
+ self.surface = pygame.Surface(size)
54
+ self.velocity = Vector2(start_vel)
55
+ if color:
56
+ self.surface.fill(color)
57
+ self.start()
58
+
59
+ def start(self):
60
+ pass
61
+
62
+ def update(self, dt):
63
+ super().update(dt)
64
+ self.rect.center += self.velocity * dt
65
+ self.update_surface()
66
+
67
+ def update_surface(self):
68
+ self.surface.set_alpha(255 - int(self.timer.get_progression() * 255))
69
+
70
+
71
+ class DirectionalParticle(BasicParticle):
72
+ def start(self):
73
+ self.original_surface = self.surface.copy()
74
+
75
+ def update_surface(self):
76
+ angle = self.velocity.angle_to(Vector2(1, 0))
77
+ self.surface = pygame.transform.rotate(self.original_surface, angle)
78
+ super().update_surface()
79
+
80
+
81
+ class ParticleGenerator(bf.Drawable):
82
+ def __init__(self) -> None:
83
+ super().__init__((0, 0))
84
+ self.particles: list[Particle] = []
85
+ self.count = 0
86
+
87
+ def get_debug_outlines(self):
88
+ return
89
+ for particle in self.particles:
90
+ yield (
91
+ particle.rect.move(particle.rect.w // 2, particle.rect.h // 2),
92
+ "blue",
93
+ )
94
+ yield (self.rect, "cyan")
95
+
96
+ def add_particle(self, particle:Particle):
97
+ particle.generator = self
98
+ particle.do_when_added()
99
+ self.particles.append(particle)
100
+ self.count += 1
101
+
102
+ def clear(self):
103
+ self.particles = []
104
+ self.count = 0
105
+ def update(self, dt: float):
106
+ particles_to_remove = []
107
+ for particle in self.particles:
108
+ particle.update(dt)
109
+ if particle.dead:
110
+ particles_to_remove.append(particle)
111
+ for p in particles_to_remove:
112
+ self.particles.remove(p)
113
+ self.count -= 1
114
+
115
+ def draw(self, camera) -> None:
116
+ camera.surface.fblits(
117
+ [(p.surface, camera.world_to_screen(p.rect)) for p in self.particles]
118
+ )
@@ -1,79 +1,79 @@
1
- from .easingController import EasingController
2
- import pygame
3
- from typing import Callable, Any, Self
4
- import batFramework as bf
5
-
6
- class PropertyEaser(EasingController):
7
- class EasedProperty:
8
- def __init__(self, getter: Callable[[], Any], setter: Callable[[Any], None], end_value: Any, start_value: Any = None):
9
- self.getter = getter
10
- self.setter = setter
11
- self.start_value = start_value if start_value is not None else getter()
12
- self.end_value = end_value
13
-
14
- def interpolate(self, t: float):
15
- a, b = self.start_value, self.end_value
16
- if isinstance(a, (int, float)):
17
- return a + (b - a) * t
18
- elif isinstance(a, pygame.Vector2):
19
- return a.lerp(b, t)
20
- elif isinstance(a, pygame.Color):
21
- return pygame.Color(
22
- round(a.r + (b.r - a.r) * t),
23
- round(a.g + (b.g - a.g) * t),
24
- round(a.b + (b.b - a.b) * t),
25
- round(a.a + (b.a - a.a) * t),
26
- )
27
- else:
28
- raise TypeError(f"Unsupported type for interpolation: {type(a)}")
29
-
30
- def apply(self, t: float):
31
- self.setter(self.interpolate(t))
32
-
33
- def __init__(
34
- self,
35
- duration: float = 1,
36
- easing: bf.easing = bf.easing.LINEAR,
37
- loop: int = 0,
38
- register: str = "global",
39
- end_callback: Callable[[], Any] = None,
40
- ):
41
- self.properties: list[PropertyEaser.EasedProperty] = []
42
-
43
- def update_all(progress):
44
- for prop in self.properties:
45
- prop.apply(progress)
46
-
47
- super().__init__(duration, easing, update_all, end_callback, loop, register)
48
-
49
- def __str__(self):
50
- return f"(PROP){super().__str__()}"
51
-
52
- def add_attr(
53
- self,
54
- obj: Any,
55
- attr: str,
56
- end_value: Any,
57
- start_value: Any = None,
58
- )->Self:
59
- self.properties.append(
60
- PropertyEaser.EasedProperty(
61
- getter=lambda o=obj, a=attr: getattr(o, a),
62
- setter=lambda v, o=obj, a=attr: setattr(o, a, v),
63
- end_value=end_value,
64
- start_value=start_value,
65
- )
66
- )
67
- return self
68
-
69
- def add_custom(
70
- self,
71
- getter: Callable[[], Any],
72
- setter: Callable[[Any], None],
73
- end_value: Any,
74
- start_value: Any = None,
75
- )->Self:
76
- self.properties.append(
77
- PropertyEaser.EasedProperty(getter, setter, end_value, start_value)
78
- )
79
- return self
1
+ from .easingController import EasingController
2
+ import pygame
3
+ from typing import Callable, Any, Self
4
+ import batFramework as bf
5
+
6
+ class PropertyEaser(EasingController):
7
+ class EasedProperty:
8
+ def __init__(self, getter: Callable[[], Any], setter: Callable[[Any], None], end_value: Any, start_value: Any = None):
9
+ self.getter = getter
10
+ self.setter = setter
11
+ self.start_value = start_value if start_value is not None else getter()
12
+ self.end_value = end_value
13
+
14
+ def interpolate(self, t: float):
15
+ a, b = self.start_value, self.end_value
16
+ if isinstance(a, (int, float)):
17
+ return a + (b - a) * t
18
+ elif isinstance(a, pygame.Vector2):
19
+ return a.lerp(b, t)
20
+ elif isinstance(a, pygame.Color):
21
+ return pygame.Color(
22
+ round(a.r + (b.r - a.r) * t),
23
+ round(a.g + (b.g - a.g) * t),
24
+ round(a.b + (b.b - a.b) * t),
25
+ round(a.a + (b.a - a.a) * t),
26
+ )
27
+ else:
28
+ raise TypeError(f"Unsupported type for interpolation: {type(a)}")
29
+
30
+ def apply(self, t: float):
31
+ self.setter(self.interpolate(t))
32
+
33
+ def __init__(
34
+ self,
35
+ duration: float = 1,
36
+ easing: bf.easing = bf.easing.LINEAR,
37
+ loop: int = 0,
38
+ register: str = "global",
39
+ end_callback: Callable[[], Any] = None,
40
+ ):
41
+ self.properties: list[PropertyEaser.EasedProperty] = []
42
+
43
+ def update_all(progress):
44
+ for prop in self.properties:
45
+ prop.apply(progress)
46
+
47
+ super().__init__(duration, easing, update_all, end_callback, loop, register)
48
+
49
+ def __str__(self):
50
+ return f"(PROP){super().__str__()}"
51
+
52
+ def add_attr(
53
+ self,
54
+ obj: Any,
55
+ attr: str,
56
+ end_value: Any,
57
+ start_value: Any = None,
58
+ )->Self:
59
+ self.properties.append(
60
+ PropertyEaser.EasedProperty(
61
+ getter=lambda o=obj, a=attr: getattr(o, a),
62
+ setter=lambda v, o=obj, a=attr: setattr(o, a, v),
63
+ end_value=end_value,
64
+ start_value=start_value,
65
+ )
66
+ )
67
+ return self
68
+
69
+ def add_custom(
70
+ self,
71
+ getter: Callable[[], Any],
72
+ setter: Callable[[Any], None],
73
+ end_value: Any,
74
+ start_value: Any = None,
75
+ )->Self:
76
+ self.properties.append(
77
+ PropertyEaser.EasedProperty(getter, setter, end_value, start_value)
78
+ )
79
+ return self