batframework 1.0.9a6__py3-none-any.whl → 1.0.9a8__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.
- batFramework/__init__.py +20 -11
- batFramework/action.py +1 -1
- batFramework/animatedSprite.py +47 -116
- batFramework/animation.py +30 -5
- batFramework/audioManager.py +16 -13
- batFramework/baseScene.py +240 -0
- batFramework/camera.py +4 -0
- batFramework/constants.py +6 -1
- batFramework/cutscene.py +221 -21
- batFramework/cutsceneManager.py +5 -2
- batFramework/drawable.py +7 -5
- batFramework/easingController.py +10 -11
- batFramework/entity.py +21 -2
- batFramework/enums.py +48 -33
- batFramework/gui/__init__.py +3 -1
- batFramework/gui/animatedLabel.py +10 -2
- batFramework/gui/button.py +4 -31
- batFramework/gui/clickableWidget.py +42 -30
- batFramework/gui/constraints/constraints.py +212 -136
- batFramework/gui/container.py +72 -48
- batFramework/gui/debugger.py +12 -17
- batFramework/gui/draggableWidget.py +8 -11
- batFramework/gui/image.py +3 -10
- batFramework/gui/indicator.py +73 -1
- batFramework/gui/interactiveWidget.py +117 -100
- batFramework/gui/label.py +73 -63
- batFramework/gui/layout.py +221 -452
- batFramework/gui/meter.py +21 -7
- batFramework/gui/radioButton.py +0 -1
- batFramework/gui/root.py +99 -29
- batFramework/gui/selector.py +257 -0
- batFramework/gui/shape.py +13 -5
- batFramework/gui/slider.py +260 -93
- batFramework/gui/textInput.py +45 -21
- batFramework/gui/toggle.py +70 -52
- batFramework/gui/tooltip.py +30 -0
- batFramework/gui/widget.py +203 -125
- batFramework/manager.py +7 -8
- batFramework/particle.py +4 -1
- batFramework/propertyEaser.py +79 -0
- batFramework/renderGroup.py +17 -50
- batFramework/resourceManager.py +43 -13
- batFramework/scene.py +15 -335
- batFramework/sceneLayer.py +138 -0
- batFramework/sceneManager.py +31 -36
- batFramework/scrollingSprite.py +8 -3
- batFramework/sprite.py +1 -1
- batFramework/templates/__init__.py +1 -2
- batFramework/templates/controller.py +97 -0
- batFramework/timeManager.py +76 -22
- batFramework/transition.py +37 -103
- batFramework/utils.py +121 -3
- {batframework-1.0.9a6.dist-info → batframework-1.0.9a8.dist-info}/METADATA +24 -3
- batframework-1.0.9a8.dist-info/RECORD +66 -0
- {batframework-1.0.9a6.dist-info → batframework-1.0.9a8.dist-info}/WHEEL +1 -1
- batFramework/character.py +0 -27
- batFramework/templates/character.py +0 -43
- batFramework/templates/states.py +0 -166
- batframework-1.0.9a6.dist-info/RECORD +0 -63
- /batframework-1.0.9a6.dist-info/LICENCE → /batframework-1.0.9a8.dist-info/LICENSE +0 -0
- {batframework-1.0.9a6.dist-info → batframework-1.0.9a8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,97 @@
|
|
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()
|
batFramework/timeManager.py
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
import batFramework as bf
|
2
2
|
from typing import Callable, Union, Self,Any
|
3
3
|
|
4
|
-
|
5
4
|
class Timer:
|
6
5
|
_count: int = 0
|
7
6
|
_available_ids: set[int] = set()
|
8
7
|
|
9
|
-
def __init__(self, duration:
|
8
|
+
def __init__(self, duration: float, end_callback: Callable[[], Any], loop: int = 0, register: str = "global") -> None:
|
10
9
|
if Timer._available_ids:
|
11
10
|
self.uid = Timer._available_ids.pop()
|
12
11
|
else:
|
@@ -14,85 +13,133 @@ class Timer:
|
|
14
13
|
Timer._count += 1
|
15
14
|
|
16
15
|
self.register = register
|
17
|
-
self.duration:
|
16
|
+
self.duration: float = duration
|
18
17
|
self.end_callback = end_callback
|
19
18
|
|
20
|
-
self.elapsed_time: float =
|
19
|
+
self.elapsed_time: float = 0
|
21
20
|
self.is_over: bool = False
|
22
|
-
self.
|
21
|
+
self.loop: int = loop # Number of loops (-1 for infinite)
|
23
22
|
self.is_paused: bool = False
|
24
23
|
self.do_delete: bool = False
|
24
|
+
self.is_stopped: bool = True
|
25
25
|
|
26
|
-
def __bool__(self)->bool:
|
27
|
-
return self.elapsed_time
|
26
|
+
def __bool__(self) -> bool:
|
27
|
+
return self.elapsed_time != -1 and self.is_over
|
28
28
|
|
29
29
|
def __str__(self) -> str:
|
30
|
-
|
30
|
+
loop_info = "infinite" if self.loop == -1 else f"{self.loop} loops left"
|
31
|
+
return f"Timer ({self.uid}) {self.elapsed_time}/{self.duration} | {loop_info} {'(D) ' if self.do_delete else ''}"
|
31
32
|
|
32
33
|
def stop(self) -> Self:
|
33
|
-
|
34
|
-
|
34
|
+
"""
|
35
|
+
Cancels all progression and stops the timer.
|
36
|
+
Does not mark it for deletion and does not call the end_callback.
|
37
|
+
Prevents automatic restart if looping.
|
38
|
+
"""
|
39
|
+
self.is_stopped = True
|
35
40
|
self.is_paused = False
|
41
|
+
self.is_over = False
|
42
|
+
self.elapsed_time = 0
|
36
43
|
return self
|
37
44
|
|
38
45
|
def start(self, force: bool = False) -> Self:
|
39
|
-
|
46
|
+
"""
|
47
|
+
Starts the timer only if not already started (unless force is used, which resets it).
|
48
|
+
"""
|
49
|
+
if self.elapsed_time > 0 and not force:
|
40
50
|
return self
|
41
|
-
if not bf.TimeManager().add_timer(self,self.register):
|
51
|
+
if not bf.TimeManager().add_timer(self, self.register):
|
42
52
|
return self
|
43
53
|
self.elapsed_time = 0
|
44
54
|
self.is_paused = False
|
45
55
|
self.is_over = False
|
56
|
+
self.is_stopped = False
|
46
57
|
return self
|
47
58
|
|
48
59
|
def pause(self) -> Self:
|
60
|
+
"""
|
61
|
+
Momentarily stops the timer until resume is called.
|
62
|
+
"""
|
49
63
|
self.is_paused = True
|
50
64
|
return self
|
51
65
|
|
52
66
|
def resume(self) -> Self:
|
67
|
+
"""
|
68
|
+
Resumes from a paused state.
|
69
|
+
"""
|
53
70
|
self.is_paused = False
|
54
71
|
return self
|
55
72
|
|
56
73
|
def delete(self) -> Self:
|
74
|
+
"""
|
75
|
+
Marks the timer for deletion.
|
76
|
+
"""
|
57
77
|
self.do_delete = True
|
58
78
|
return self
|
59
79
|
|
60
80
|
def has_started(self) -> bool:
|
61
|
-
|
81
|
+
"""
|
82
|
+
Returns True if the timer has started.
|
83
|
+
"""
|
84
|
+
return not self.is_stopped
|
62
85
|
|
63
86
|
def get_progression(self) -> float:
|
64
|
-
|
87
|
+
"""
|
88
|
+
Returns the progression of the timer (0 to 1) as a float.
|
89
|
+
"""
|
90
|
+
if self.is_stopped:
|
65
91
|
return 0
|
66
92
|
if self.elapsed_time >= self.duration:
|
67
93
|
return 1
|
68
94
|
return self.elapsed_time / self.duration
|
69
95
|
|
70
96
|
def update(self, dt) -> None:
|
71
|
-
if self.
|
97
|
+
if self.is_stopped or self.is_paused or self.is_over:
|
72
98
|
return
|
73
99
|
self.elapsed_time += dt
|
74
|
-
# print("update :",self.elapsed_time,self.duration)
|
75
100
|
if self.get_progression() == 1:
|
76
101
|
self.end()
|
77
102
|
|
78
103
|
def end(self):
|
104
|
+
"""
|
105
|
+
Ends the timer progression (calls the end_callback function).
|
106
|
+
Is called automatically once the timer is over.
|
107
|
+
Will not mark the timer for deletion.
|
108
|
+
If it is looping, it will restart the timer **only if it wasn't stopped**.
|
109
|
+
"""
|
110
|
+
self.is_over = True
|
79
111
|
if self.end_callback:
|
80
112
|
self.end_callback()
|
81
|
-
|
82
|
-
|
83
|
-
if self.
|
113
|
+
|
114
|
+
# Handle looping
|
115
|
+
if self.loop == -1: # Infinite looping
|
116
|
+
self.elapsed_time = 0
|
84
117
|
self.start()
|
85
118
|
return
|
119
|
+
elif self.loop > 0: # Decrease loop count and restart
|
120
|
+
self.loop -= 1
|
121
|
+
self.elapsed_time = 0
|
122
|
+
self.start()
|
123
|
+
return
|
124
|
+
|
125
|
+
# Stop the timer if no loops are left
|
126
|
+
self.is_stopped = True
|
86
127
|
|
87
128
|
def should_delete(self) -> bool:
|
129
|
+
"""
|
130
|
+
Method that returns if the timer is to be deleted.
|
131
|
+
Required for timer management.
|
132
|
+
"""
|
88
133
|
return self.is_over or self.do_delete
|
89
134
|
|
90
135
|
def _release_id(self):
|
91
136
|
Timer._available_ids.add(self.uid)
|
92
137
|
|
93
|
-
|
94
138
|
class SceneTimer(Timer):
|
95
|
-
|
139
|
+
"""
|
140
|
+
A timer that is only updated while the given scene is active (being updated)
|
141
|
+
"""
|
142
|
+
def __init__(self, duration: float | int, end_callback, loop: int = 0, scene_name:str = "global") -> None:
|
96
143
|
super().__init__(duration, end_callback, loop, scene_name)
|
97
144
|
|
98
145
|
class TimeManager(metaclass=bf.Singleton):
|
@@ -125,6 +172,13 @@ class TimeManager(metaclass=bf.Singleton):
|
|
125
172
|
if name not in self.registers:
|
126
173
|
self.registers[name] = TimeManager.TimerRegister(active)
|
127
174
|
|
175
|
+
def remove_register(self, name):
|
176
|
+
if name not in self.registers:
|
177
|
+
return
|
178
|
+
|
179
|
+
self.registers.pop(name)
|
180
|
+
|
181
|
+
|
128
182
|
def add_timer(self, timer, register="global") -> bool:
|
129
183
|
if register in self.registers:
|
130
184
|
self.registers[register].add_timer(timer)
|
@@ -136,7 +190,7 @@ class TimeManager(metaclass=bf.Singleton):
|
|
136
190
|
return [t for t in self.registers.values() if t.active]
|
137
191
|
|
138
192
|
def update(self, dt):
|
139
|
-
for
|
193
|
+
for register in self.registers.values():
|
140
194
|
if register.active:
|
141
195
|
register.update(dt)
|
142
196
|
|
batFramework/transition.py
CHANGED
@@ -10,39 +10,23 @@ Both surfaces to transition need to be the same size
|
|
10
10
|
|
11
11
|
class Transition:
|
12
12
|
def __init__(
|
13
|
-
self, duration: float,
|
13
|
+
self, duration: float=1, easing: bf.easing = bf.easing.LINEAR
|
14
14
|
) -> None:
|
15
15
|
"""
|
16
16
|
duration : time in seconds
|
17
17
|
easing function : controls the progression rate
|
18
18
|
"""
|
19
19
|
self.duration: float = duration
|
20
|
-
self.controller = bf.EasingController(
|
21
|
-
|
22
|
-
|
23
|
-
update_callback=self.update,
|
24
|
-
end_callback=self.end,
|
20
|
+
self.controller = bf.EasingController( # main controller for the transition progression
|
21
|
+
duration,easing,
|
22
|
+
update_callback=self.update,end_callback=self.end,
|
25
23
|
)
|
26
|
-
self.start_callback : Callable[[],Any] = None
|
27
|
-
self.update_callback : Callable[[float],Any]= None
|
28
|
-
self.end_callback : Callable[[],Any]= None
|
29
24
|
self.source: pygame.Surface = None
|
30
25
|
self.dest: pygame.Surface = None
|
26
|
+
self.is_over : bool = False # this flag tells the manager the transition is over
|
31
27
|
|
32
28
|
def __repr__(self) -> str:
|
33
|
-
return f"Transition {self.__class__},{self.duration}"
|
34
|
-
|
35
|
-
def set_start_callback(self, func : Callable[[],Any]) -> Self:
|
36
|
-
self.start_callback = func
|
37
|
-
return self
|
38
|
-
|
39
|
-
def set_update_callback(self, func : Callable[[float],Any]) -> Self:
|
40
|
-
self.update_callback = func
|
41
|
-
return self
|
42
|
-
|
43
|
-
def set_end_callback(self, func : Callable[[],Any]) -> Self:
|
44
|
-
self.end_callback = func
|
45
|
-
return self
|
29
|
+
return f"Transition ({self.__class__},{self.duration})"
|
46
30
|
|
47
31
|
def set_source(self, surface: pygame.Surface) -> None:
|
48
32
|
self.source = surface
|
@@ -51,127 +35,77 @@ class Transition:
|
|
51
35
|
self.dest = surface
|
52
36
|
|
53
37
|
def start(self):
|
54
|
-
if self.controller.has_started():
|
38
|
+
if self.controller.has_started(): # can't start again while it's in progress
|
55
39
|
return
|
56
|
-
if self.duration:
|
40
|
+
if self.duration: # start the transition
|
57
41
|
self.controller.start()
|
58
|
-
if self.start_callback:
|
59
|
-
self.start_callback()
|
60
42
|
return
|
61
43
|
|
44
|
+
# if no duration the transition is instantaneous
|
62
45
|
self.controller.start()
|
63
|
-
if self.start_callback:
|
64
|
-
self.start_callback()
|
65
46
|
self.controller.end()
|
66
|
-
self.update(1)
|
47
|
+
self.update(1)# to prevent weird behaviour, update once with progression at max value
|
67
48
|
self.end()
|
68
49
|
|
69
50
|
def update(self, progression: float) -> None:
|
70
|
-
|
71
|
-
self.update_callback(progression)
|
51
|
+
pass
|
72
52
|
|
73
53
|
def end(self):
|
74
54
|
self.controller.stop()
|
75
|
-
|
76
|
-
self.end_callback()
|
55
|
+
self.is_over = True
|
77
56
|
|
78
57
|
def draw(self, surface: pygame.Surface) -> None:
|
79
58
|
pass
|
80
59
|
|
81
|
-
def skip(self
|
82
|
-
self.
|
83
|
-
|
84
|
-
self.end_callback()
|
60
|
+
def skip(self):
|
61
|
+
self.end()
|
62
|
+
|
85
63
|
|
86
64
|
|
87
65
|
class FadeColor(Transition):
|
88
|
-
def __init__(
|
89
|
-
|
90
|
-
color: tuple,
|
91
|
-
middle_duration: float,
|
92
|
-
first_duration: float | None = None,
|
93
|
-
second_duration: float | None = None,
|
94
|
-
easing_function: bf.easing = bf.easing.LINEAR,
|
95
|
-
) -> None:
|
96
|
-
super().__init__(0, easing_function)
|
97
|
-
if first_duration is None:
|
98
|
-
first_duration = middle_duration
|
99
|
-
if second_duration is None:
|
100
|
-
second_duration = middle_duration
|
101
|
-
|
102
|
-
self.first = Fade(first_duration)
|
103
|
-
self.second = Fade(second_duration)
|
66
|
+
def __init__(self,duration:float,color=(0,0,0),color_start:float=0.3,color_end:float=0.7, easing = bf.easing.LINEAR):
|
67
|
+
super().__init__(duration, easing)
|
104
68
|
self.color = color
|
105
|
-
self.
|
106
|
-
self.
|
107
|
-
|
108
|
-
self.timer = bf.Timer(middle_duration, self.transition_to_second)
|
109
|
-
self.first.set_end_callback(self.transition_to_middle)
|
110
|
-
self.second.set_end_callback(self.transition_to_end)
|
111
|
-
|
112
|
-
def transition_to_middle(self):
|
113
|
-
self.next_step(self.timer.start)
|
114
|
-
|
115
|
-
def transition_to_second(self):
|
116
|
-
self.next_step(self.second.start)
|
117
|
-
|
118
|
-
def transition_to_end(self):
|
119
|
-
self.next_step(self.end)
|
120
|
-
|
121
|
-
def next_step(self, callback : Callable[[],Any]=None) -> None:
|
122
|
-
self.index += 1
|
123
|
-
if callback:
|
124
|
-
callback()
|
125
|
-
|
126
|
-
def set_source(self, surface) -> None:
|
127
|
-
super().set_source(surface)
|
128
|
-
self.first.set_source(surface)
|
129
|
-
|
130
|
-
def set_dest(self, surface) -> None:
|
131
|
-
super().set_dest(surface)
|
132
|
-
self.second.set_dest(surface)
|
69
|
+
self.color_start = color_start
|
70
|
+
self.color_end = color_end
|
133
71
|
|
134
72
|
def start(self):
|
135
|
-
|
136
|
-
self.start_callback()
|
137
|
-
|
73
|
+
super().start()
|
138
74
|
self.color_surf = pygame.Surface(self.source.get_size())
|
139
75
|
self.color_surf.fill(self.color)
|
140
76
|
|
141
|
-
self.first.set_dest(self.color_surf)
|
142
|
-
self.second.set_source(self.color_surf)
|
143
|
-
self.first.start()
|
144
|
-
|
145
77
|
def draw(self, surface):
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
self.
|
152
|
-
|
153
|
-
def skip(self, no_callback: bool = False):
|
154
|
-
if not no_callback and self.end_callback:
|
155
|
-
self.end_callback()
|
156
|
-
|
157
|
-
self.first.controller.stop()
|
158
|
-
self.timer.stop()
|
159
|
-
self.second.controller.stop()
|
78
|
+
v = self.controller.get_value()
|
79
|
+
if v < self.color_start:
|
80
|
+
v = v/(self.color_start)
|
81
|
+
self.color_surf.set_alpha(255*v)
|
82
|
+
surface.blit(self.source)
|
83
|
+
surface.blit(self.color_surf)
|
160
84
|
|
85
|
+
elif v < self.color_end:
|
86
|
+
self.color_surf.set_alpha(255)
|
87
|
+
surface.blit(self.color_surf)
|
161
88
|
|
89
|
+
else:
|
90
|
+
v = (v-self.color_end)/(1-self.color_end)
|
91
|
+
surface.blit(self.color_surf)
|
92
|
+
self.dest.set_alpha(255*v)
|
93
|
+
surface.blit(self.dest)
|
162
94
|
|
163
95
|
class Fade(Transition):
|
164
96
|
def end(self):
|
165
97
|
self.dest.set_alpha(255)
|
166
98
|
return super().end()
|
167
99
|
|
100
|
+
def start(self):
|
101
|
+
super().start()
|
102
|
+
|
168
103
|
def draw(self, surface):
|
169
104
|
dest_alpha = 255 * self.controller.get_value()
|
170
105
|
self.dest.set_alpha(dest_alpha)
|
171
106
|
surface.blit(self.source, (0, 0))
|
172
107
|
surface.blit(self.dest, (0, 0))
|
173
108
|
|
174
|
-
|
175
109
|
class GlideRight(Transition):
|
176
110
|
def draw(self, surface):
|
177
111
|
width = surface.get_width()
|
batFramework/utils.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import pygame
|
2
2
|
import batFramework as bf
|
3
|
+
import math
|
3
4
|
import random
|
4
5
|
from .enums import *
|
5
6
|
import re
|
@@ -8,7 +9,7 @@ from functools import cache
|
|
8
9
|
if TYPE_CHECKING:
|
9
10
|
from .drawable import Drawable
|
10
11
|
from .entity import Entity
|
11
|
-
|
12
|
+
from pygame.math import Vector2
|
12
13
|
|
13
14
|
|
14
15
|
class Singleton(type):
|
@@ -236,12 +237,129 @@ class Utils:
|
|
236
237
|
|
237
238
|
Args:
|
238
239
|
margin (int): Margin from the screen edges, where the point won't be generated.
|
239
|
-
If margin is less than 0 or greater than the screen resolution, returns (0, 0).
|
240
|
+
If margin is less than 0 or greater than half the screen resolution, returns (0, 0).
|
240
241
|
|
241
242
|
Returns:
|
242
243
|
tuple[int, int]: A tuple representing a random point (x, y) on the screen within the screen
|
243
244
|
resolution minus the margin.
|
244
245
|
"""
|
245
|
-
if margin < 0 or margin > bf.const.RESOLUTION[0] or margin > bf.const.RESOLUTION[1]:
|
246
|
+
if margin < 0 or margin > bf.const.RESOLUTION[0]//2 or margin > bf.const.RESOLUTION[1]//2:
|
246
247
|
return 0, 0
|
247
248
|
return random.randint(margin, bf.const.RESOLUTION[0] - margin), random.randint(margin, bf.const.RESOLUTION[1] - margin)
|
249
|
+
|
250
|
+
@staticmethod
|
251
|
+
def distance_point(a:tuple[float,float],b:tuple[float,float]):
|
252
|
+
return math.sqrt((a[0]-b[0]) ** 2 + (a[1]-b[1])**2)
|
253
|
+
|
254
|
+
@staticmethod
|
255
|
+
def rotate_point(point: Vector2, angle: float, center: Vector2) -> Vector2:
|
256
|
+
"""Rotate a point around a center by angle (in degrees)."""
|
257
|
+
rad = math.radians(angle)
|
258
|
+
translated = point - center
|
259
|
+
rotated = Vector2(
|
260
|
+
translated.x * math.cos(rad) - translated.y * math.sin(rad),
|
261
|
+
translated.x * math.sin(rad) + translated.y * math.cos(rad)
|
262
|
+
)
|
263
|
+
return rotated + center
|
264
|
+
|
265
|
+
|
266
|
+
def draw_triangle(surface:pygame.Surface, color, rect:pygame.FRect|pygame.Rect, direction:bf.enums.direction=bf.enums.direction.RIGHT,width:int=0):
|
267
|
+
"""
|
268
|
+
Draw a filled triangle inside a rectangle on a Pygame surface, pointing in the specified direction.
|
269
|
+
|
270
|
+
Args:
|
271
|
+
surface: The Pygame surface to draw on.
|
272
|
+
color: The color of the triangle (e.g., (255, 0, 0) for red).
|
273
|
+
rect: A pygame.Rect object defining the rectangle's position and size.
|
274
|
+
direction: A string ('up', 'down', 'left', 'right') indicating the triangle's orientation.
|
275
|
+
"""
|
276
|
+
# Define the three vertices of the triangle based on direction
|
277
|
+
rect = rect.copy()
|
278
|
+
rect.inflate_ip(-1,-1)
|
279
|
+
if direction == direction.UP:
|
280
|
+
points = [
|
281
|
+
(rect.left, rect.bottom), # Bottom-left corner
|
282
|
+
(rect.right, rect.bottom), # Bottom-right corner
|
283
|
+
(rect.centerx, rect.top) # Top center (apex)
|
284
|
+
]
|
285
|
+
elif direction == direction.DOWN:
|
286
|
+
points = [
|
287
|
+
(rect.left, rect.top), # Top-left corner
|
288
|
+
(rect.right, rect.top), # Top-right corner
|
289
|
+
(rect.centerx, rect.bottom) # Bottom center (apex)
|
290
|
+
]
|
291
|
+
elif direction == direction.LEFT:
|
292
|
+
points = [
|
293
|
+
(rect.right, rect.top), # Top-right corner
|
294
|
+
(rect.right, rect.bottom), # Bottom-right corner
|
295
|
+
(rect.left, rect.centery) # Left center (apex)
|
296
|
+
]
|
297
|
+
elif direction == direction.RIGHT:
|
298
|
+
points = [
|
299
|
+
(rect.left, rect.top), # Top-left corner
|
300
|
+
(rect.left, rect.bottom), # Bottom-left corner
|
301
|
+
(rect.right, rect.centery) # Right center (apex)
|
302
|
+
]
|
303
|
+
else:
|
304
|
+
raise ValueError("Invalid direction")
|
305
|
+
|
306
|
+
# Draw the filled triangle
|
307
|
+
pygame.draw.polygon(surface, color, points,width=width)
|
308
|
+
|
309
|
+
def draw_arc_by_points(surface, color, start_pos, end_pos, tightness=0.5, width=1, resolution=0.5):
|
310
|
+
"""
|
311
|
+
Draw a smooth circular arc connecting start_pos and end_pos.
|
312
|
+
`tightness` controls curvature: 0 is straight line, 1 is semicircle, higher = more bulge.
|
313
|
+
Negative tightness flips the bulge direction.
|
314
|
+
|
315
|
+
Args:
|
316
|
+
surface - pygame Surface
|
317
|
+
color - RGB or RGBA
|
318
|
+
start_pos - (x, y)
|
319
|
+
end_pos - (x, y)
|
320
|
+
tightness - curvature control, 0 = straight, 1 = half circle
|
321
|
+
width - line width
|
322
|
+
resolution - approx pixels per segment
|
323
|
+
Returns:
|
324
|
+
pygame.Rect bounding the drawn arc
|
325
|
+
"""
|
326
|
+
p0 = pygame.Vector2(start_pos)
|
327
|
+
p1 = pygame.Vector2(end_pos)
|
328
|
+
chord = p1 - p0
|
329
|
+
if chord.length_squared() == 0:
|
330
|
+
return pygame.draw.circle(surface, color, p0, width // 2)
|
331
|
+
|
332
|
+
# Midpoint and perpendicular
|
333
|
+
mid = (p0 + p1) * 0.5
|
334
|
+
perp = pygame.Vector2(-chord.y, chord.x).normalize()
|
335
|
+
|
336
|
+
# Distance of center from midpoint, based on tightness
|
337
|
+
h = chord.length() * tightness
|
338
|
+
center = mid + perp * h
|
339
|
+
|
340
|
+
# Radius and angles
|
341
|
+
r = (p0 - center).length()
|
342
|
+
ang0 = math.atan2(p0.y - center.y, p0.x - center.x)
|
343
|
+
ang1 = math.atan2(p1.y - center.y, p1.x - center.x)
|
344
|
+
|
345
|
+
# Normalize sweep direction based on sign of tightness
|
346
|
+
sweep = ang1 - ang0
|
347
|
+
if tightness > 0 and sweep < 0:
|
348
|
+
sweep += 2 * math.pi
|
349
|
+
elif tightness < 0 and sweep > 0:
|
350
|
+
sweep -= 2 * math.pi
|
351
|
+
|
352
|
+
# Number of points
|
353
|
+
arc_len = abs(sweep * r)
|
354
|
+
segs = max(2, int(arc_len / max(resolution, 1)))
|
355
|
+
|
356
|
+
points = []
|
357
|
+
for i in range(segs + 1):
|
358
|
+
t = i / segs
|
359
|
+
a = ang0 + sweep * t
|
360
|
+
points.append((
|
361
|
+
center.x + math.cos(a) * r,
|
362
|
+
center.y + math.sin(a) * r
|
363
|
+
))
|
364
|
+
|
365
|
+
return pygame.draw.lines(surface, color, False, points, width)
|
@@ -1,15 +1,36 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: batframework
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.9a8
|
4
4
|
Summary: Pygame framework for making games easier.
|
5
5
|
Author-email: Turan Baturay <baturayturan@gmail.com>
|
6
|
+
License: MIT License
|
7
|
+
|
8
|
+
Copyright (c) [2023] [TURAN BATURAY]
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.
|
6
27
|
Project-URL: Homepage, https://github.com/TuranBaturay/batFramework
|
7
28
|
Classifier: Programming Language :: Python :: 3
|
8
29
|
Classifier: License :: OSI Approved :: MIT License
|
9
30
|
Classifier: Operating System :: OS Independent
|
10
31
|
Requires-Python: >=3.11
|
11
32
|
Description-Content-Type: text/markdown
|
12
|
-
License-File:
|
33
|
+
License-File: LICENSE
|
13
34
|
Requires-Dist: pygame-ce
|
14
35
|
|
15
36
|
# batFramework
|