batframework 1.0.8a7__py3-none-any.whl → 1.0.8a8__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 (70) hide show
  1. batFramework/__init__.py +51 -68
  2. batFramework/action.py +99 -126
  3. batFramework/actionContainer.py +9 -53
  4. batFramework/animatedSprite.py +82 -141
  5. batFramework/audioManager.py +26 -69
  6. batFramework/camera.py +69 -259
  7. batFramework/constants.py +54 -16
  8. batFramework/cutscene.py +29 -39
  9. batFramework/cutsceneBlocks.py +43 -36
  10. batFramework/debugger.py +48 -0
  11. batFramework/dynamicEntity.py +9 -18
  12. batFramework/easing.py +71 -0
  13. batFramework/entity.py +97 -48
  14. batFramework/gui/__init__.py +2 -10
  15. batFramework/gui/button.py +78 -9
  16. batFramework/gui/constraints.py +204 -0
  17. batFramework/gui/container.py +32 -174
  18. batFramework/gui/debugger.py +43 -131
  19. batFramework/gui/frame.py +19 -0
  20. batFramework/gui/image.py +20 -56
  21. batFramework/gui/indicator.py +21 -38
  22. batFramework/gui/interactiveWidget.py +13 -192
  23. batFramework/gui/label.py +74 -309
  24. batFramework/gui/layout.py +63 -231
  25. batFramework/gui/root.py +38 -134
  26. batFramework/gui/shape.py +57 -237
  27. batFramework/gui/toggle.py +51 -101
  28. batFramework/gui/widget.py +250 -358
  29. batFramework/manager.py +19 -52
  30. batFramework/particles.py +77 -0
  31. batFramework/scene.py +123 -281
  32. batFramework/sceneManager.py +116 -178
  33. batFramework/stateMachine.py +8 -11
  34. batFramework/time.py +58 -145
  35. batFramework/transition.py +124 -195
  36. batFramework/transitionManager.py +0 -0
  37. batFramework/triggerZone.py +1 -1
  38. batFramework/utils.py +147 -112
  39. batframework-1.0.8a8.dist-info/METADATA +53 -0
  40. batframework-1.0.8a8.dist-info/RECORD +42 -0
  41. {batframework-1.0.8a7.dist-info → batframework-1.0.8a8.dist-info}/WHEEL +1 -1
  42. batFramework/character.py +0 -27
  43. batFramework/easingController.py +0 -58
  44. batFramework/enums.py +0 -113
  45. batFramework/fontManager.py +0 -65
  46. batFramework/gui/clickableWidget.py +0 -220
  47. batFramework/gui/constraints/__init__.py +0 -1
  48. batFramework/gui/constraints/constraints.py +0 -815
  49. batFramework/gui/dialogueBox.py +0 -99
  50. batFramework/gui/draggableWidget.py +0 -40
  51. batFramework/gui/meter.py +0 -74
  52. batFramework/gui/radioButton.py +0 -84
  53. batFramework/gui/slider.py +0 -240
  54. batFramework/gui/style.py +0 -10
  55. batFramework/gui/styleManager.py +0 -48
  56. batFramework/gui/textInput.py +0 -247
  57. batFramework/object.py +0 -123
  58. batFramework/particle.py +0 -115
  59. batFramework/renderGroup.py +0 -67
  60. batFramework/resourceManager.py +0 -100
  61. batFramework/scrollingSprite.py +0 -114
  62. batFramework/sprite.py +0 -51
  63. batFramework/templates/__init__.py +0 -2
  64. batFramework/templates/character.py +0 -44
  65. batFramework/templates/states.py +0 -166
  66. batFramework/tileset.py +0 -46
  67. batframework-1.0.8a7.dist-info/LICENCE +0 -21
  68. batframework-1.0.8a7.dist-info/METADATA +0 -43
  69. batframework-1.0.8a7.dist-info/RECORD +0 -62
  70. {batframework-1.0.8a7.dist-info → batframework-1.0.8a8.dist-info}/top_level.txt +0 -0
batFramework/__init__.py CHANGED
@@ -1,83 +1,66 @@
1
1
  import pygame
2
- import batFramework as bf
3
- import sys
4
2
  from .constants import Constants as const
3
+ import os
4
+ import json
5
+ initialized = False
6
+
7
+ def init(
8
+ resolution:tuple[int,int],
9
+ flags:int=0,
10
+ vsync:int = 0,
11
+ default_text_size=None,
12
+ default_font=None,
13
+ resource_path:str|None=None,
14
+ window_title:str="BatFramework Project",
15
+ fps_limit : int = 0
16
+ ):
17
+ global initialized
18
+ if not initialized:
19
+ pygame.init()
20
+ pygame.display.set_caption(window_title)
21
+
22
+ # Initialize display
23
+ const.init_screen(resolution,flags,vsync)
24
+ const.set_fps_limit(fps_limit)
25
+
26
+ # Initialize default text size
27
+ if default_text_size: const.set_default_text_size(default_text_size)
28
+ # Initialize resource path for game data
29
+ if resource_path: const.set_resource_path(resource_path)
30
+
31
+ # Initialize default font cache
32
+ from .utils import Utils
33
+ if default_font is None or isinstance(default_font,str):
34
+ Utils.init_font(default_font)
35
+ else:
36
+ raise ValueError(f"default_font '{default_font}' can be either string or None")
37
+
38
+ f = list(Utils.FONTS[None].values())[0]
39
+ print(f"Set default font to : {f.name} {'' if default_font is not None else '(default value)'}")
40
+ initialized = True
41
+
42
+ from .constants import Colors as color
43
+ from .constants import Axis as axis
5
44
  from .utils import Singleton
6
- from .enums import *
7
- from .resourceManager import ResourceManager
8
- from .fontManager import FontManager
9
45
  from .utils import Utils as utils
10
- from .tileset import Tileset
11
- from .time import *
12
- from .easingController import EasingController
13
- from .cutscene import Cutscene, CutsceneManager
46
+ from .time import Time, Timer
47
+ from .cutscene import Cutscene,CutsceneManager
14
48
  from .cutsceneBlocks import *
49
+ from .easing import Easing, EasingAnimation
15
50
  from .audioManager import AudioManager
16
- import batFramework.transition as transition
51
+ from .utils import Layout, Alignment, Direction
52
+ from .transition import *
17
53
  from .action import Action
18
- from .actionContainer import *
54
+ from .actionContainer import ActionContainer
19
55
  from .camera import Camera
20
- from .object import Object
21
56
  from .entity import Entity
22
- from .renderGroup import RenderGroup
23
57
  from .dynamicEntity import DynamicEntity
24
- from .sprite import Sprite
25
- from .scrollingSprite import ScrollingSprite
26
- from .particle import *
27
- from .animatedSprite import AnimatedSprite, Animation
28
- from .character import Character
58
+ from .animatedSprite import AnimatedSprite, AnimState
29
59
  from .stateMachine import State, StateMachine
60
+ from .particles import Particle, ParticleManager
61
+ # from .debugger import Debugger
30
62
  from .scene import Scene
31
- from .gui import *
63
+ from .gui import *
32
64
  from .sceneManager import SceneManager
33
65
  from .manager import Manager
34
- from .templates import *
35
- import importlib.metadata
36
-
37
-
38
- def init_screen(resolution: tuple[int, int], flags: int = 0, vsync: int = 0):
39
- const.RESOLUTION = resolution
40
- const.FLAGS = flags
41
- const.VSYNC = vsync
42
- const.SCREEN = pygame.display.set_mode(
43
- const.RESOLUTION, const.FLAGS, vsync=const.VSYNC
44
- )
45
- print(
46
- f"Window : {resolution[0]}x{resolution[1]} [vsync:{pygame.display.is_vsync()}]"
47
- )
48
-
49
-
50
-
51
- def print_version():
52
- package_name = "batFramework"
53
- try:
54
- version = importlib.metadata.version(package_name)
55
- print(f"{package_name} version: {version}")
56
- except importlib.metadata.PackageNotFoundError:
57
- print(f"{package_name} is not installed")
58
-
59
-
60
- def init(
61
- resolution: tuple[int, int],
62
- flags: int = 0,
63
- vsync: int = 0,
64
- default_text_size=None,
65
- default_font=None,
66
- resource_path: str | None = None,
67
- window_title: str = "BatFramework Project",
68
- fps_limit: int = 0,
69
- ):
70
- print_version()
71
- pygame.display.set_caption(window_title)
72
- init_screen(resolution, flags, vsync)
73
66
 
74
- ResourceManager().set_resource_path(
75
- resource_path if resource_path is not None else "."
76
- )
77
- if resource_path is not None:
78
- ResourceManager().load_dir(ResourceManager().RESOURCE_PATH)
79
- if default_text_size is not None:
80
- FontManager().set_default_text_size(default_text_size)
81
- FontManager().init_font(default_font)
82
- const.BF_INITIALIZED = True
83
- const.set_fps_limit(fps_limit)
batFramework/action.py CHANGED
@@ -1,8 +1,8 @@
1
- from typing import Any, Self
1
+ from typing import Any
2
2
  from enum import Enum
3
3
  import pygame
4
- from .enums import actionType
5
4
 
5
+ ActionType = Enum("type", "INSTANTANEOUS CONTINUOUS HOLDING")
6
6
 
7
7
  class Action:
8
8
  def __init__(self, name: str) -> None:
@@ -12,28 +12,34 @@ class Action:
12
12
  Args:
13
13
  name (str): The name of the action.
14
14
  """
15
- self.name: str = name
16
- self.active: bool = False
17
- self.data: dict = {}
18
- self.consume_event: bool = False
19
- self._type: actionType = actionType.INSTANTANEOUS
20
- self._key_control: set = set()
21
- self._mouse_control: set = set()
22
- self._event_control: set = set()
23
- self._gamepad_button_control: set = set()
24
- self._gamepad_axis_control: set = set()
15
+ self._name = name
16
+ self._active = False
17
+ self._type = ActionType.INSTANTANEOUS
18
+ self._key_control = set()
19
+ self._mouse_control = set()
20
+ self._gamepad_button_control = set()
21
+ self._gamepad_axis_control = set()
25
22
  self._holding = set()
23
+ self._unique = True
24
+ self.data : Any = None
26
25
 
27
- def set_consume_event(self, val: bool) -> Self:
26
+ def set_unique(self, val: bool) -> None:
28
27
  """
29
28
  Set whether this action is unique (exclusive).
30
- When in an action Container, unique actions -when active - break the propagation of their event to other actions.
31
29
 
32
30
  Args:
33
31
  val (bool): True if the action is unique, False otherwise.
34
32
  """
35
- self.consume_event = val
36
- return self
33
+ self._unique = val
34
+
35
+ def is_active(self) -> bool:
36
+ """
37
+ Check if the action is currently active.
38
+
39
+ Returns:
40
+ bool: True if the action is active, False otherwise.
41
+ """
42
+ return self._active
37
43
 
38
44
  def set_active(self, value: bool) -> None:
39
45
  """
@@ -42,18 +48,10 @@ class Action:
42
48
  Args:
43
49
  value (bool): True to activate the action, False to deactivate it.
44
50
  """
45
- self.active = value
46
- # self._holding = set()
47
-
48
- def add_event_control(self, *events) -> Self:
49
- self._event_control.update(events)
50
- return self
51
-
52
- def remove_event_control(self, *events) -> Self:
53
- self._event_control = self._event_control - events
54
- return self
51
+ self._active = value
52
+ self._holding = set()
55
53
 
56
- def add_key_control(self, *keys) -> Self:
54
+ def add_key_control(self, *keys) -> 'Action':
57
55
  """
58
56
  Add key controls to the action.
59
57
 
@@ -66,51 +64,29 @@ class Action:
66
64
  self._key_control.update(keys)
67
65
  return self
68
66
 
69
- def remove_key_control(self, *keys: int) -> Self:
67
+ def add_mouse_control(self, *mouse_buttons) -> 'Action':
70
68
  """
71
- Remove key controls to the action.
69
+ Add mouse control to the action.
72
70
 
73
71
  Args:
74
- *keys (int): Key codes to control this action.
72
+ *mouse_buttons (int): Mouse button codes to control this action.
75
73
 
76
74
  Returns:
77
75
  Action: The updated Action object for method chaining.
78
76
  """
79
- self._key_control = self._key_control - set(keys)
77
+ self._mouse_control.update(mouse_buttons)
80
78
  return self
81
79
 
82
- def replace_key_control(self, key, new_key) -> Self:
83
- if not key in self._key_control:
84
- return self
85
- self.remove_key_control(key)
86
- self.add_key_control(new_key)
87
- return self
88
-
89
- def add_mouse_control(self, *mouse: int) -> Self:
80
+ def get_name(self) -> str:
90
81
  """
91
- Add mouse control to the action.
92
-
93
- Args:
94
- *mouse_buttons (int): Mouse button codes to control this action.
82
+ Get the name of the action.
95
83
 
96
84
  Returns:
97
- Action: The updated Action object for method chaining.
85
+ str: The name of the action.
98
86
  """
99
- self._mouse_control.update(mouse)
100
- return self
101
-
102
- def remove_mouse_control(self, *mouse: int) -> Self:
103
- self._mouse_control = self._mouse_control - set(mouse)
104
- return self
87
+ return self._name
105
88
 
106
- def replace_mouse_control(self, mouse, new_mouse) -> Self:
107
- if not mouse in self._mouse_control:
108
- return self
109
- self.remove_mouse_control(mouse)
110
- self.add_mouse_control(new_mouse)
111
- return self
112
-
113
- def set_continuous(self) -> Self:
89
+ def set_continuous(self) -> 'Action':
114
90
  """
115
91
  Set the action type to continuous.
116
92
 
@@ -118,7 +94,7 @@ class Action:
118
94
  Action: The updated Action object for method chaining.
119
95
  """
120
96
  self._holding = set()
121
- self._type = actionType.CONTINUOUS
97
+ self._type = ActionType.CONTINUOUS
122
98
  return self
123
99
 
124
100
  def is_continuous(self) -> bool:
@@ -128,16 +104,16 @@ class Action:
128
104
  Returns:
129
105
  bool: True if the action type is continuous, False otherwise.
130
106
  """
131
- return self._type == actionType.CONTINUOUS
107
+ return self._type == ActionType.CONTINUOUS
132
108
 
133
- def set_instantaneous(self) -> Self:
109
+ def set_instantaneous(self) -> 'Action':
134
110
  """
135
111
  Set the action type to instantaneous.
136
112
 
137
113
  Returns:
138
114
  Action: The updated Action object for method chaining.
139
115
  """
140
- self._type = actionType.INSTANTANEOUS
116
+ self._type = ActionType.INSTANTANEOUS
141
117
  self._holding = set()
142
118
  return self
143
119
 
@@ -148,16 +124,16 @@ class Action:
148
124
  Returns:
149
125
  bool: True if the action type is instantaneous, False otherwise.
150
126
  """
151
- return self._type == actionType.INSTANTANEOUS
127
+ return self._type == ActionType.INSTANTANEOUS
152
128
 
153
- def set_holding(self) -> Self:
129
+ def set_holding(self) -> 'Action':
154
130
  """
155
131
  Set the action type to holding.
156
132
 
157
133
  Returns:
158
134
  Action: The updated Action object for method chaining.
159
135
  """
160
- self._type = actionType.HOLDING
136
+ self._type = ActionType.HOLDING
161
137
  return self
162
138
 
163
139
  def is_holding_type(self) -> bool:
@@ -167,17 +143,13 @@ class Action:
167
143
  Returns:
168
144
  bool: True if the action type is holding, False otherwise.
169
145
  """
170
- return self._type == actionType.HOLDING
146
+ return self._type == ActionType.HOLDING
171
147
 
172
- def process_update(self, event: pygame.Event) -> None:
173
- if (
174
- event.type == pygame.MOUSEMOTION
175
- and self._type == actionType.HOLDING
176
- and pygame.MOUSEMOTION in self._mouse_control
177
- ) or self._event_control:
178
- self.data = event.dict
148
+ def process_update(self,event:pygame.Event)->None:
149
+ if self.is_active() and event.type == pygame.MOUSEMOTION and self.is_holding_type() and pygame.MOUSEMOTION in self._mouse_control:
150
+ self.data = {"pos":event.pos,"rel":event.rel}
179
151
 
180
- def process_activate(self, event: pygame.event.Event):
152
+ def process_activate(self, event: pygame.event.Event) -> bool:
181
153
  """
182
154
  Process activation of the action based on a pygame event.
183
155
 
@@ -187,93 +159,94 @@ class Action:
187
159
  Returns:
188
160
  bool: True if the action was activated by the event, False otherwise.
189
161
  """
190
-
191
162
  if event.type == pygame.KEYDOWN and event.key in self._key_control:
192
- self._activate_action(event.key)
163
+ self._active = True
164
+ if self.is_holding_type():
165
+ self._holding.add(event.key)
166
+ return True
193
167
 
194
- elif (
195
- event.type == pygame.MOUSEBUTTONDOWN and event.button in self._mouse_control
196
- ):
197
- self._activate_action(event.button)
168
+ if event.type == pygame.MOUSEBUTTONDOWN and event.button in self._mouse_control:
169
+ self._active = True
170
+ if self.is_holding_type():
171
+ self._holding.add(event.button)
172
+ return True
198
173
 
199
- elif event.type == pygame.MOUSEMOTION and event.type in self._mouse_control:
200
- self._activate_action(event.type)
174
+ if event.type == pygame.MOUSEMOTION and event.type in self._mouse_control:
175
+ self._active = True
176
+ if self.is_holding_type():
177
+ self._holding.add(event.type)
178
+ return True
201
179
 
202
- elif event.type in self._event_control:
203
- self._activate_action(event.type)
204
- self.data = event.dict
205
- else:
206
- return
207
- if self.consume_event:
208
- event.consumed = True
180
+ return False
209
181
 
210
- def _activate_action(self, control):
211
- self.active = True
212
- if self._type == actionType.HOLDING:
213
- self._holding.add(control)
214
-
215
- def process_deactivate(self, event: pygame.event.Event):
182
+ def process_deactivate(self, event: pygame.event.Event) -> bool:
216
183
  """
217
184
  Process deactivation of the action based on a pygame event.
218
185
 
219
186
  Args:
220
187
  event (pygame.event.Event): The pygame event to process.
221
188
 
189
+ Returns:
190
+ bool: True if the action was deactivated by the event, False otherwise.
222
191
  """
223
- if self._type == actionType.HOLDING:
192
+ if self._type == ActionType.HOLDING:
224
193
  if event.type == pygame.KEYUP and event.key in self._key_control:
225
- self._deactivate_action(event.key)
194
+ if event.key in self._holding:
195
+ self._holding.remove(event.key)
196
+ if not self._holding:
197
+ self._active = False
198
+ return True
226
199
  elif (
227
200
  event.type == pygame.MOUSEBUTTONUP
228
201
  and event.button in self._mouse_control
229
202
  ):
230
- self._deactivate_action(event.button)
231
- elif event.type == pygame.MOUSEMOTION and event.type in self._mouse_control:
232
- self._deactivate_action(event.type)
233
- elif event.type in self._event_control:
234
- self._deactivate_action(event.type)
235
- else:
236
- event.consumed = False
237
-
238
- if self.consume_event:
239
- event.consumed = True
203
+ if event.button in self._holding:
204
+ self._holding.remove(event.button)
205
+ if not self._holding:
206
+ self._active = False
207
+ return True
208
+ elif (
209
+ event.type == pygame.MOUSEMOTION
210
+ and event.type in self._mouse_control
211
+ ):
212
+ self.value = None
213
+ if event.type in self._holding:
214
+ self._holding.remove(event.type)
215
+ if not self._holding:
216
+ self._active = False
217
+ return True
240
218
 
241
- def _deactivate_action(self, control) -> bool:
242
- if control in self._holding:
243
- self._holding.remove(control)
244
- if not self._holding:
245
- self.active = False
219
+ return False
246
220
 
247
- def process_event(self, event: pygame.event.Event):
221
+ def process_event(self, event: pygame.event.Event) -> bool:
248
222
  """
249
223
  Process a pygame event and update the action's state.
250
224
 
251
225
  Args:
252
226
  event (pygame.event.Event): The pygame event to process.
253
- """
254
227
 
255
- if event.consumed:
256
- return
257
- if not self.active:
258
- self.process_activate(event)
228
+ Returns:
229
+ bool: True if the action state changed, False otherwise.
230
+ """
231
+ if not self._active:
232
+ res = self.process_activate(event)
259
233
  else:
260
- self.process_deactivate(event)
261
- if self.active:
262
- self.process_update(event)
263
- return
234
+ res = self.process_deactivate(event)
235
+ self.process_update(event)
236
+ return res
264
237
 
265
238
  def reset(self) -> None:
266
239
  """
267
240
  Reset the action's state to the default state.
268
241
  """
269
- if self._type in {actionType.CONTINUOUS, actionType.HOLDING}:
242
+ if self._type in {ActionType.CONTINUOUS, ActionType.HOLDING}:
270
243
  return
271
- elif self._type == actionType.INSTANTANEOUS:
272
- self.active = False
244
+ elif self._type == ActionType.INSTANTANEOUS:
245
+ self._active = False
273
246
 
274
247
  def hard_reset(self) -> None:
275
248
  """
276
249
  Hard reset the action, deactivating it and clearing any holding controls.
277
250
  """
278
- self.active = False
251
+ self._active = False
279
252
  self._holding = set()
@@ -1,44 +1,32 @@
1
1
  import batFramework as bf
2
- import pygame
3
2
 
4
3
 
5
4
  class ActionContainer:
6
5
  def __init__(self, *actions: list[bf.Action]) -> None:
7
6
  self._actions: dict[str, bf.Action] = {}
8
7
  if actions:
9
- self.add_actions(*actions)
10
-
11
- def __iter__(self):
12
- return iter(self._actions.values())
8
+ self.add_action(*actions)
13
9
 
14
10
  def clear(self):
15
11
  self._actions = {}
16
12
 
17
- def add_actions(self, *actions: bf.Action):
13
+ def add_action(self, *actions: bf.Action):
18
14
  for action in actions:
19
- self._actions[action.name] = action
15
+ self._actions[action.get_name()] = action
20
16
 
21
- def get(self, name: str) -> bf.Action:
17
+ def get(self,name:str)->bf.Action:
22
18
  return self._actions.get(name)
23
19
 
24
- def has_action(self, name: str):
20
+ def has_action(self, name:str):
25
21
  return name in self._actions
26
22
 
27
- def get_all(self) -> list[bf.Action]:
28
- return self._actions
29
-
30
- def is_active(self, *names: str) -> bool:
31
- return all(
32
- self._actions.get(name).active if name in self._actions else False
33
- for name in names
34
- )
23
+ def is_active(self, *names:str)->bool:
24
+ return all(self._actions.get(name).is_active() if name in self._actions else False for name in names)
35
25
 
36
26
  def process_event(self, event):
37
- if event.consumed:
38
- return
39
27
  for action in self._actions.values():
40
- action.process_event(event)
41
- if event.consumed == True:
28
+ a = action.process_event(event)
29
+ if a and action._unique:
42
30
  break
43
31
 
44
32
  def reset(self):
@@ -48,35 +36,3 @@ class ActionContainer:
48
36
  def hard_reset(self):
49
37
  for action in self._actions.values():
50
38
  action.hard_reset()
51
-
52
-
53
- class DirectionalKeyControls(ActionContainer):
54
- def __init__(self):
55
- super().__init__(
56
- bf.Action("up").add_key_control(pygame.K_UP).set_holding(),
57
- bf.Action("down").add_key_control(pygame.K_DOWN).set_holding(),
58
- bf.Action("left").add_key_control(pygame.K_LEFT).set_holding(),
59
- bf.Action("right").add_key_control(pygame.K_RIGHT).set_holding(),
60
- )
61
-
62
-
63
- class WASDControls(ActionContainer):
64
- def __init__(self):
65
- super().__init__(
66
- bf.Action("up").add_key_control(pygame.K_w).set_holding(),
67
- bf.Action("down").add_key_control(pygame.K_s).set_holding(),
68
- bf.Action("left").add_key_control(pygame.K_a).set_holding(),
69
- bf.Action("right").add_key_control(pygame.K_d).set_holding(),
70
- )
71
-
72
-
73
- class HybridControls(ActionContainer):
74
- def __init__(self):
75
- super().__init__(
76
- bf.Action("up").add_key_control(pygame.K_UP, pygame.K_w).set_holding(),
77
- bf.Action("down").add_key_control(pygame.K_DOWN, pygame.K_s).set_holding(),
78
- bf.Action("left").add_key_control(pygame.K_LEFT, pygame.K_a).set_holding(),
79
- bf.Action("right")
80
- .add_key_control(pygame.K_RIGHT, pygame.K_r)
81
- .set_holding(),
82
- )