batframework 0.1.13__tar.gz → 1.0.1__tar.gz

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 (75) hide show
  1. batframework-1.0.1/LICENCE +21 -0
  2. {batframework-0.1.13 → batframework-1.0.1}/PKG-INFO +3 -2
  3. {batframework-0.1.13 → batframework-1.0.1}/pyproject.toml +2 -2
  4. batframework-1.0.1/src/batFramework/__init__.py +62 -0
  5. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/action.py +42 -20
  6. batframework-1.0.1/src/batFramework/actionContainer.py +77 -0
  7. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/animatedSprite.py +26 -20
  8. batframework-1.0.1/src/batFramework/camera.py +253 -0
  9. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/constants.py +26 -51
  10. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/cutscene.py +15 -15
  11. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/cutsceneBlocks.py +11 -9
  12. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/dynamicEntity.py +7 -6
  13. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/easing.py +28 -23
  14. batframework-1.0.1/src/batFramework/entity.py +160 -0
  15. batframework-1.0.1/src/batFramework/enums.py +14 -0
  16. batframework-1.0.1/src/batFramework/fontManager.py +57 -0
  17. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/gui/__init__.py +2 -2
  18. batframework-1.0.1/src/batFramework/gui/button.py +138 -0
  19. batframework-1.0.1/src/batFramework/gui/constraints.py +262 -0
  20. batframework-1.0.1/src/batFramework/gui/container.py +60 -0
  21. batframework-1.0.1/src/batFramework/gui/debugger.py +97 -0
  22. batframework-1.0.1/src/batFramework/gui/frame.py +25 -0
  23. batframework-1.0.1/src/batFramework/gui/image.py +43 -0
  24. batframework-1.0.1/src/batFramework/gui/indicator.py +46 -0
  25. batframework-1.0.1/src/batFramework/gui/interactiveWidget.py +23 -0
  26. batframework-1.0.1/src/batFramework/gui/label.py +152 -0
  27. batframework-1.0.1/src/batFramework/gui/layout.py +96 -0
  28. batframework-1.0.1/src/batFramework/gui/root.py +78 -0
  29. batframework-1.0.1/src/batFramework/gui/shape.py +79 -0
  30. batframework-1.0.1/src/batFramework/gui/slider.py +5 -0
  31. batframework-1.0.1/src/batFramework/gui/toggle.py +66 -0
  32. batframework-1.0.1/src/batFramework/gui/widget.py +341 -0
  33. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/manager.py +18 -13
  34. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/particles.py +16 -13
  35. batframework-1.0.1/src/batFramework/resourceManager.py +55 -0
  36. batframework-1.0.1/src/batFramework/scene.py +309 -0
  37. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/sceneManager.py +21 -16
  38. batframework-1.0.1/src/batFramework/sprite.py +31 -0
  39. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/stateMachine.py +1 -0
  40. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/tileset.py +7 -9
  41. batframework-1.0.1/src/batFramework/time.py +86 -0
  42. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/transition.py +20 -12
  43. batframework-1.0.1/src/batFramework/utils.py +61 -0
  44. {batframework-0.1.13 → batframework-1.0.1}/src/batframework.egg-info/PKG-INFO +3 -2
  45. {batframework-0.1.13 → batframework-1.0.1}/src/batframework.egg-info/SOURCES.txt +6 -1
  46. batframework-0.1.13/src/batFramework/__init__.py +0 -67
  47. batframework-0.1.13/src/batFramework/actionContainer.py +0 -38
  48. batframework-0.1.13/src/batFramework/camera.py +0 -123
  49. batframework-0.1.13/src/batFramework/debugger.py +0 -48
  50. batframework-0.1.13/src/batFramework/entity.py +0 -122
  51. batframework-0.1.13/src/batFramework/gui/button.py +0 -87
  52. batframework-0.1.13/src/batFramework/gui/constraints.py +0 -229
  53. batframework-0.1.13/src/batFramework/gui/container.py +0 -61
  54. batframework-0.1.13/src/batFramework/gui/debugger.py +0 -47
  55. batframework-0.1.13/src/batFramework/gui/frame.py +0 -25
  56. batframework-0.1.13/src/batFramework/gui/image.py +0 -23
  57. batframework-0.1.13/src/batFramework/gui/indicator.py +0 -42
  58. batframework-0.1.13/src/batFramework/gui/interactiveWidget.py +0 -22
  59. batframework-0.1.13/src/batFramework/gui/label.py +0 -148
  60. batframework-0.1.13/src/batFramework/gui/layout.py +0 -93
  61. batframework-0.1.13/src/batFramework/gui/root.py +0 -65
  62. batframework-0.1.13/src/batFramework/gui/shape.py +0 -86
  63. batframework-0.1.13/src/batFramework/gui/toggle.py +0 -62
  64. batframework-0.1.13/src/batFramework/gui/widget.py +0 -321
  65. batframework-0.1.13/src/batFramework/scene.py +0 -251
  66. batframework-0.1.13/src/batFramework/time.py +0 -75
  67. batframework-0.1.13/src/batFramework/utils.py +0 -124
  68. {batframework-0.1.13 → batframework-1.0.1}/README.md +0 -0
  69. {batframework-0.1.13 → batframework-1.0.1}/setup.cfg +0 -0
  70. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/audioManager.py +0 -0
  71. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/transitionManager.py +0 -0
  72. {batframework-0.1.13 → batframework-1.0.1}/src/batFramework/triggerZone.py +0 -0
  73. {batframework-0.1.13 → batframework-1.0.1}/src/batframework.egg-info/dependency_links.txt +0 -0
  74. {batframework-0.1.13 → batframework-1.0.1}/src/batframework.egg-info/requires.txt +0 -0
  75. {batframework-0.1.13 → batframework-1.0.1}/src/batframework.egg-info/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2023] [TURAN BATURAY]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,14 +1,15 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: batframework
3
- Version: 0.1.13
3
+ Version: 1.0.1
4
4
  Summary: Pygame framework for making games easier.
5
5
  Author-email: Turan Baturay <baturayturan@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/TuranBaturay/batFramework
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: License :: OSI Approved :: MIT License
9
9
  Classifier: Operating System :: OS Independent
10
- Requires-Python: >=3.10
10
+ Requires-Python: >=3.11
11
11
  Description-Content-Type: text/markdown
12
+ License-File: LICENCE
12
13
  Requires-Dist: pygame-ce
13
14
 
14
15
  # batFramework & gamejam Project
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "batframework"
7
- version = "0.1.13"
7
+ version = "1.0.1"
8
8
  authors = [
9
9
  { name="Turan Baturay", email="baturayturan@gmail.com" }
10
10
  ]
@@ -15,7 +15,7 @@ classifiers = [
15
15
  "License :: OSI Approved :: MIT License",
16
16
  "Operating System :: OS Independent",
17
17
  ]
18
- requires-python = ">=3.10"
18
+ requires-python = ">=3.11"
19
19
  dependencies=[
20
20
  "pygame-ce"
21
21
  ]
@@ -0,0 +1,62 @@
1
+ import pygame
2
+ import batFramework as bf
3
+ import sys
4
+ from .constants import Constants as const
5
+ from .constants import Colors as color
6
+ from .utils import Singleton
7
+ from .enums import *
8
+ from .resourceManager import ResourceManager
9
+ from .fontManager import FontManager
10
+ # if const.BF_INITIALIZED:
11
+ from .utils import Utils as utils
12
+ from .tileset import Tileset
13
+ from .time import TimeManager, Timer
14
+ from .cutscene import Cutscene, CutsceneManager
15
+ from .cutsceneBlocks import *
16
+ from .easing import Easing, EasingAnimation
17
+ from .audioManager import AudioManager
18
+ # import batFramework.transition as transition
19
+ from .action import Action
20
+ from .actionContainer import *
21
+ from .camera import Camera
22
+ from .entity import Entity
23
+ from .dynamicEntity import DynamicEntity
24
+ from .sprite import Sprite
25
+ from .animatedSprite import AnimatedSprite, AnimState
26
+ from .stateMachine import State, StateMachine
27
+ # from .debugger import Debugger
28
+ from .scene import Scene
29
+ from .gui import *
30
+ from .sceneManager import SceneManager
31
+ from .manager import Manager
32
+
33
+
34
+ def init_screen(resolution: tuple[int, int], flags: int = 0, vsync: int = 0):
35
+ const.RESOLUTION = resolution
36
+ const.FLAGS = flags
37
+ const.VSYNC = vsync
38
+ const.SCREEN = pygame.display.set_mode(
39
+ const.RESOLUTION,const.FLAGS, vsync=const.VSYNC
40
+ )
41
+ print(
42
+ f"Window : {resolution[0]}x{resolution[1]} [vsync:{pygame.display.is_vsync()}]"
43
+ )
44
+
45
+ def init(
46
+ resolution: tuple[int, int],
47
+ flags: int = 0,
48
+ vsync: int = 0,
49
+ default_text_size=None,
50
+ default_font=None,
51
+ resource_path: str | None = None,
52
+ window_title: str = "BatFramework Project",
53
+ fps_limit: int = 0):
54
+ pygame.display.set_caption(window_title)
55
+ init_screen(resolution,flags,vsync)
56
+
57
+ ResourceManager().set_resource_path(resource_path if resource_path is not None else ".")
58
+ ResourceManager().load_dir(ResourceManager().RESOURCE_PATH)
59
+ if default_text_size is not None : FontManager().set_default_text_size(default_text_size)
60
+ FontManager().init_font(default_font)
61
+ const.BF_INITIALIZED = True
62
+ const.set_fps_limit(fps_limit)
@@ -12,20 +12,21 @@ class Action:
12
12
  Args:
13
13
  name (str): The name of the action.
14
14
  """
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()
15
+ self._name :str= name
16
+ self._active :bool= False
17
+ self._type :ActionType = ActionType.INSTANTANEOUS
18
+ self._key_control : set= set()
19
+ self._mouse_control :set = set()
20
+ self._gamepad_button_control :set = set()
21
+ self._gamepad_axis_control :set = set()
22
22
  self._holding = set()
23
23
  self._unique = True
24
- self.data : Any = None
24
+ self.data: Any = None
25
25
 
26
26
  def set_unique(self, val: bool) -> None:
27
27
  """
28
28
  Set whether this action is unique (exclusive).
29
+ When in an action Container, unique actions -when active - break the propagation of their event to other actions.
29
30
 
30
31
  Args:
31
32
  val (bool): True if the action is unique, False otherwise.
@@ -51,7 +52,7 @@ class Action:
51
52
  self._active = value
52
53
  self._holding = set()
53
54
 
54
- def add_key_control(self, *keys) -> 'Action':
55
+ def add_key_control(self, *keys) -> "Action":
55
56
  """
56
57
  Add key controls to the action.
57
58
 
@@ -64,7 +65,26 @@ class Action:
64
65
  self._key_control.update(keys)
65
66
  return self
66
67
 
67
- def add_mouse_control(self, *mouse_buttons) -> 'Action':
68
+ def remove_key_control(self, *keys:int) -> "Action":
69
+ """
70
+ Remove key controls to the action.
71
+
72
+ Args:
73
+ *keys (int): Key codes to control this action.
74
+
75
+ Returns:
76
+ Action: The updated Action object for method chaining.
77
+ """
78
+ self._key_control = self._key_control - set(keys)
79
+ return self
80
+
81
+ def replace_key_control(self, key, new_key) -> "Action":
82
+ if not key in self._key_control : return self
83
+ self.remove_key_control(key)
84
+ self.add_key_control(new_key)
85
+ return self
86
+
87
+ def add_mouse_control(self, *mouse_buttons:int) -> "Action":
68
88
  """
69
89
  Add mouse control to the action.
70
90
 
@@ -86,7 +106,7 @@ class Action:
86
106
  """
87
107
  return self._name
88
108
 
89
- def set_continuous(self) -> 'Action':
109
+ def set_continuous(self) -> "Action":
90
110
  """
91
111
  Set the action type to continuous.
92
112
 
@@ -106,7 +126,7 @@ class Action:
106
126
  """
107
127
  return self._type == ActionType.CONTINUOUS
108
128
 
109
- def set_instantaneous(self) -> 'Action':
129
+ def set_instantaneous(self) -> "Action":
110
130
  """
111
131
  Set the action type to instantaneous.
112
132
 
@@ -126,7 +146,7 @@ class Action:
126
146
  """
127
147
  return self._type == ActionType.INSTANTANEOUS
128
148
 
129
- def set_holding(self) -> 'Action':
149
+ def set_holding(self) -> "Action":
130
150
  """
131
151
  Set the action type to holding.
132
152
 
@@ -145,9 +165,14 @@ class Action:
145
165
  """
146
166
  return self._type == ActionType.HOLDING
147
167
 
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}
168
+ def process_update(self, event: pygame.Event) -> None:
169
+ if (
170
+ self.is_active()
171
+ and event.type == pygame.MOUSEMOTION
172
+ and self.is_holding_type()
173
+ and pygame.MOUSEMOTION in self._mouse_control
174
+ ):
175
+ self.data = {"pos": event.pos, "rel": event.rel}
151
176
 
152
177
  def process_activate(self, event: pygame.event.Event) -> bool:
153
178
  """
@@ -205,10 +230,7 @@ class Action:
205
230
  if not self._holding:
206
231
  self._active = False
207
232
  return True
208
- elif (
209
- event.type == pygame.MOUSEMOTION
210
- and event.type in self._mouse_control
211
- ):
233
+ elif event.type == pygame.MOUSEMOTION and event.type in self._mouse_control:
212
234
  self.value = None
213
235
  if event.type in self._holding:
214
236
  self._holding.remove(event.type)
@@ -0,0 +1,77 @@
1
+ import batFramework as bf
2
+ import pygame
3
+
4
+
5
+ class ActionContainer:
6
+ def __init__(self, *actions: list[bf.Action]) -> None:
7
+ self._actions: dict[str, bf.Action] = {}
8
+ if actions:
9
+ self.add_action(*actions)
10
+
11
+ def clear(self):
12
+ self._actions = {}
13
+
14
+ def add_action(self, *actions: bf.Action):
15
+ for action in actions:
16
+ self._actions[action.get_name()] = action
17
+
18
+ def get(self, name: str) -> bf.Action:
19
+ return self._actions.get(name)
20
+
21
+ def has_action(self, name: str):
22
+ return name in self._actions
23
+
24
+ def get_all(self) -> list[bf.Action]:
25
+ return self._actions
26
+
27
+ def is_active(self, *names: str) -> bool:
28
+ return all(
29
+ self._actions.get(name).is_active() if name in self._actions else False
30
+ for name in names
31
+ )
32
+
33
+ def process_event(self, event):
34
+ for action in self._actions.values():
35
+ a = action.process_event(event)
36
+ if a and action._unique:
37
+ break
38
+
39
+ def reset(self):
40
+ for action in self._actions.values():
41
+ action.reset()
42
+
43
+ def hard_reset(self):
44
+ for action in self._actions.values():
45
+ action.hard_reset()
46
+
47
+
48
+ class DirectionalKeyControls(ActionContainer):
49
+ def __init__(self):
50
+ super().__init__(
51
+ bf.Action("up").add_key_control(pygame.K_UP).set_holding(),
52
+ bf.Action("down").add_key_control(pygame.K_DOWN).set_holding(),
53
+ bf.Action("left").add_key_control(pygame.K_LEFT).set_holding(),
54
+ bf.Action("right").add_key_control(pygame.K_RIGHT).set_holding(),
55
+ )
56
+
57
+
58
+ class WASDControls(ActionContainer):
59
+ def __init__(self):
60
+ super().__init__(
61
+ bf.Action("up").add_key_control(pygame.K_w).set_holding(),
62
+ bf.Action("down").add_key_control(pygame.K_s).set_holding(),
63
+ bf.Action("left").add_key_control(pygame.K_a).set_holding(),
64
+ bf.Action("right").add_key_control(pygame.K_d).set_holding(),
65
+ )
66
+
67
+
68
+ class HybridControls(ActionContainer):
69
+ def __init__(self):
70
+ super().__init__(
71
+ bf.Action("up").add_key_control(pygame.K_UP, pygame.K_w).set_holding(),
72
+ bf.Action("down").add_key_control(pygame.K_DOWN, pygame.K_s).set_holding(),
73
+ bf.Action("left").add_key_control(pygame.K_LEFT, pygame.K_a).set_holding(),
74
+ bf.Action("right")
75
+ .add_key_control(pygame.K_RIGHT, pygame.K_r)
76
+ .set_holding(),
77
+ )
@@ -2,7 +2,6 @@ import batFramework as bf
2
2
  import pygame
3
3
 
4
4
 
5
-
6
5
  def search_index(target, lst):
7
6
  cumulative_sum = 0
8
7
  for index, value in enumerate(lst):
@@ -13,12 +12,14 @@ def search_index(target, lst):
13
12
 
14
13
 
15
14
  class AnimState:
16
- def __init__(self,name:str, file, width, height, frame_length_list:list|int) -> None:
15
+ def __init__(
16
+ self, name: str, file, width, height, frame_length_list: list | int
17
+ ) -> None:
17
18
  self.frames: list[pygame.Surface] = bf.utils.img_slice(file, width, height)
18
19
  self.frames_flipX: list[pygame.Surface] = bf.utils.img_slice(
19
20
  file, width, height, True
20
21
  )
21
- self.name= name
22
+ self.name = name
22
23
  self.frame_length_list = []
23
24
  self.ffl_length = 0
24
25
  self.set_frame_length_list(frame_length_list)
@@ -26,34 +27,33 @@ class AnimState:
26
27
  def __repr__(self):
27
28
  return f"AnimState({self.name})"
28
29
 
29
- def get_frame_index(self, counter:float|int):
30
+ def get_frame_index(self, counter: float | int):
30
31
  return search_index(int(counter % self.ffl_length), self.frame_length_list)
31
32
 
32
33
  def get_frame(self, counter, flip):
33
34
  i = self.get_frame_index(counter)
34
35
  return self.frames_flipX[i] if flip else self.frames[i]
35
36
 
36
- def set_frame_length_list(self,frame_length_list:list[int]|int):
37
- if isinstance(frame_length_list,int):
37
+ def set_frame_length_list(self, frame_length_list: list[int] | int):
38
+ if isinstance(frame_length_list, int):
38
39
  frame_length_list = [frame_length_list] * len(self.frames)
39
- if len(frame_length_list) != len(self.frames) :
40
+ if len(frame_length_list) != len(self.frames):
40
41
  raise ValueError("frame_length_list should have values for all frames")
41
42
  self.frame_length_list = frame_length_list
42
43
  self.ffl_length = sum(self.frame_length_list)
43
-
44
+
44
45
 
45
46
  class AnimatedSprite(bf.DynamicEntity):
46
47
  def __init__(self, size=None) -> None:
47
48
  super().__init__(size, no_surface=True)
48
49
  self.float_counter = 0
49
50
  self.animStates: dict[str, AnimState] = {}
50
- self.current_animState :str = ""
51
+ self.current_animState: str = ""
51
52
  self.flipX = False
52
53
  self._locked = False
53
54
 
54
- def set_counter(self,value:float):
55
+ def set_counter(self, value: float):
55
56
  self.float_counter = value
56
-
57
57
 
58
58
  def lock_animState(self):
59
59
  self._locked = True
@@ -64,20 +64,25 @@ class AnimatedSprite(bf.DynamicEntity):
64
64
  def set_flipX(self, value):
65
65
  self.flipX = value
66
66
 
67
- def remove_animState(self, name:str):
68
- if not name in self.animStates :return
69
- self.animStates.pop(name)
70
- if self.current_animState == name : self.current_animState = list(self.animStates.keys())[0] if self.animStates else ""
67
+ def remove_animState(self, name: str):
68
+ if not name in self.animStates:
69
+ return
70
+ self.animStates.pop(name)
71
+ if self.current_animState == name:
72
+ self.current_animState = (
73
+ list(self.animStates.keys())[0] if self.animStates else ""
74
+ )
71
75
 
72
76
  def add_animState(
73
77
  self, name: str, file: str, size: tuple[int, int], frame_length_list: list[int]
74
78
  ):
75
79
  if name in self.animStates:
76
80
  return
77
- self.animStates[name] = AnimState(name,file, *size, frame_length_list)
78
- if len(self.animStates) == 1 : self.set_animState(name)
81
+ self.animStates[name] = AnimState(name, file, *size, frame_length_list)
82
+ if len(self.animStates) == 1:
83
+ self.set_animState(name)
79
84
 
80
- def set_animState(self, state:str, reset_counter=True, lock=False):
85
+ def set_animState(self, state: str, reset_counter=True, lock=False):
81
86
  if state not in self.animStates or self._locked:
82
87
  return False
83
88
  self.current_animState = state
@@ -95,7 +100,7 @@ class AnimatedSprite(bf.DynamicEntity):
95
100
  return True
96
101
 
97
102
  def get_state(self):
98
- return self.animStates.get(self.current_animState,None)
103
+ return self.animStates.get(self.current_animState, None)
99
104
 
100
105
  def get_frame_index(self):
101
106
  return self.animStates[self.current_animState].get_frame_index(
@@ -103,7 +108,8 @@ class AnimatedSprite(bf.DynamicEntity):
103
108
  )
104
109
 
105
110
  def update(self, dt: float):
106
- if not self.animStates : return
111
+ if not self.animStates:
112
+ return
107
113
  self.float_counter += 60 * dt
108
114
  if self.float_counter > self.get_state().ffl_length:
109
115
  self.float_counter = 0
@@ -0,0 +1,253 @@
1
+ import pygame
2
+ from pygame.math import Vector2
3
+ import math
4
+ import batFramework as bf
5
+
6
+
7
+ class Camera:
8
+ _transform_cache = {} # Cache for transformed surfaces
9
+
10
+ def __init__(self, flags=0, size: tuple[int,int] | None = None,convert_alpha:bool=False) -> None:
11
+ """
12
+ Initialize the Camera object.
13
+
14
+ Args:
15
+ flags (int): Flags for camera initialization.
16
+ size (tuple): Optional size for camera (defaults to window size)
17
+ """
18
+ # Initialize camera attributes
19
+ size = size if size else bf.const.RESOLUTION
20
+ self.rect = pygame.Rect(0, 0, *size)
21
+ self.flags: int = flags
22
+ self.blit_special_flags: int = pygame.BLEND_ALPHA_SDL2
23
+ self._clear_color: pygame.Color = pygame.Color(0, 0, 0, 0)
24
+ self.zoom_factor = 1
25
+ self.cached_surfaces: dict[float, pygame.Surface] = {}
26
+ self.surface: pygame.Surface = pygame.Surface((0, 0))
27
+ if convert_alpha :
28
+ self.surface = self.surface.convert_alpha()
29
+ else:
30
+ self.surface = self.surface.convert()
31
+ self.follow_point_func = None
32
+ self.max_zoom = 2
33
+ self.min_zoom = 0.1
34
+ self.zoom(1)
35
+
36
+ def set_clear_color(self, color: pygame.Color | tuple | str) -> "Camera":
37
+ """
38
+ Set the clear color for the camera surface.
39
+
40
+ Args:
41
+ color (pygame.Color | tuple): Color to set as the clear color.
42
+
43
+ Returns:
44
+ Camera: Returns the Camera object.
45
+ """
46
+ if not isinstance(color, pygame.Color):
47
+ color = pygame.Color(color)
48
+ self._clear_color = color
49
+ return self
50
+
51
+ def set_max_zoom(self, value: float) -> "Camera":
52
+ """
53
+ Set the maximum zoom value for the camera.
54
+
55
+ Args:
56
+ value (float): Maximum zoom value.
57
+
58
+ Returns:
59
+ Camera: Returns the Camera object.
60
+ """
61
+ self.max_zoom = value
62
+ return self
63
+
64
+ def set_min_zoom(self, value: float) -> "Camera":
65
+ """
66
+ Set the minimum zoom value for the camera.
67
+
68
+ Args:
69
+ value (float): Minimum zoom value.
70
+
71
+ Returns:
72
+ Camera: Returns the Camera object.
73
+ """
74
+ self.min_zoom = value
75
+ return self
76
+
77
+ def clear(self) -> None:
78
+ """
79
+ Clear the camera surface with the set clear color.
80
+ """
81
+ self.surface.fill(self._clear_color)
82
+
83
+ def get_center(self) -> tuple[float,float]:
84
+ """
85
+ Get the center of the camera's view.
86
+
87
+ Returns:
88
+ Vector2: Returns the center coordinates.
89
+ """
90
+ return self.rect.center
91
+
92
+ def move(self, x, y) -> "Camera":
93
+ """
94
+ Moves the camera rect by the given coordinates.
95
+
96
+ Args:
97
+ x: X-coordinate to move.
98
+ y: Y-coordinate to move.
99
+
100
+ Returns:
101
+ Camera: Returns the Camera object.
102
+ """
103
+ self.rect.topleft += Vector2(x, y)
104
+ return self
105
+
106
+ def set_position(self, x, y) -> "Camera":
107
+ """
108
+ Set the camera rect top-left position.
109
+
110
+ Args:
111
+ x: X-coordinate to set.
112
+ y: Y-coordinate to set.
113
+
114
+ Returns:
115
+ Camera: Returns the Camera object.
116
+ """
117
+ self.rect.topleft = (x, y)
118
+ return self
119
+
120
+ def set_center(self, x, y) -> "Camera":
121
+ """
122
+ Set the camera rect center position.
123
+
124
+ Args:
125
+ x: X-coordinate for the center.
126
+ y: Y-coordinate for the center.
127
+
128
+ Returns:
129
+ Camera: Returns the Camera object.
130
+ """
131
+ self.rect.center = (x, y)
132
+ return self
133
+
134
+ def set_follow_point(self, func) -> "Camera":
135
+ """
136
+ Set the following function (returns tuple x y).
137
+ Camera will center its position to the center of the given coordinates.
138
+
139
+ Args:
140
+ func: Function returning coordinates to follow.
141
+
142
+ Returns:
143
+ Camera: Returns the Camera object.
144
+ """
145
+ self.follow_point_func = func
146
+ return self
147
+
148
+ def zoom_by(self, amount: float) -> "Camera":
149
+ """
150
+ Zooms the camera by the given amount.
151
+
152
+ Args:
153
+ amount (float): Amount to zoom.
154
+
155
+ Returns:
156
+ Camera: Returns the Camera object.
157
+ """
158
+ self.zoom(self.zoom_factor + amount)
159
+ return self
160
+
161
+ def zoom(self, factor) -> "Camera":
162
+ """
163
+ Zooms the camera to the given factor.
164
+
165
+ Args:
166
+ factor: Factor to set for zooming.
167
+
168
+ Returns:
169
+ Camera: Returns the Camera object.
170
+ """
171
+ if factor < self.min_zoom or factor > self.max_zoom:
172
+ return self
173
+
174
+ factor = round(factor, 2)
175
+ self.zoom_factor = factor
176
+
177
+ if factor not in self.cached_surfaces:
178
+ self.cached_surfaces[factor] = pygame.Surface(
179
+ tuple(i / factor for i in bf.const.RESOLUTION), flags=self.flags
180
+ ).convert_alpha()
181
+ self.cached_surfaces[factor].fill((0, 0, 0, 0))
182
+
183
+ self.surface = self.cached_surfaces[self.zoom_factor]
184
+ self.rect = self.surface.get_rect(center=self.rect.center)
185
+ return self
186
+
187
+ def intersects(self, rect: pygame.Rect | pygame.FRect) -> bool:
188
+ """
189
+ Check if the camera view intersects with the given rectangle.
190
+
191
+ Args:
192
+ rect (pygame.Rect | pygame.FRect): Rectangle to check intersection with.
193
+
194
+ Returns:
195
+ bool: True if intersection occurs, False otherwise.
196
+ """
197
+ return (
198
+ self.rect.x < rect.right
199
+ and self.rect.right > rect.x
200
+ and self.rect.y < rect.bottom
201
+ and self.rect.bottom > rect.y
202
+ )
203
+
204
+ def transpose(self, rect: pygame.Rect | pygame.FRect) -> pygame.Rect | pygame.FRect:
205
+ """
206
+ Transpose the given rectangle coordinates relative to the camera.
207
+
208
+ Args:
209
+ rect (pygame.Rect | pygame.FRect): Rectangle to transpose.
210
+
211
+ Returns:
212
+ pygame.FRect: Transposed rectangle.
213
+ """
214
+ return pygame.FRect(rect.x - self.rect.left, rect.y - self.rect.top, *rect.size)
215
+
216
+ def convert_screen_to_world(self, x, y):
217
+ """
218
+ Convert screen coordinates to world coordinates based on camera settings.
219
+
220
+ Args:
221
+ x: X-coordinate in screen space.
222
+ y: Y-coordinate in screen space.
223
+
224
+ Returns:
225
+ tuple: Converted world coordinates.
226
+ """
227
+ return x / self.zoom_factor + self.rect.x, y / self.zoom_factor + self.rect.y
228
+
229
+ def update(self, dt):
230
+ """
231
+ Update the camera position based on the follow point function.
232
+
233
+ Args:
234
+ dt: Time delta for updating the camera position.
235
+ """
236
+ if self.follow_point_func:
237
+ target = self.follow_point_func()
238
+ self.rect.center = Vector2(self.rect.center).lerp(target, ((dt * 60) * 0.1))
239
+
240
+ def draw(self, surface: pygame.Surface):
241
+ """
242
+ Draw the camera view onto the provided surface with proper scaling.
243
+
244
+ Args:
245
+ surface (pygame.Surface): Surface to draw the camera view onto.
246
+ """
247
+ if self.zoom_factor == 1:
248
+ surface.blit(self.surface, (0, 0), special_flags=self.blit_special_flags)
249
+ return
250
+
251
+ # Scale the surface to match the resolution
252
+ scaled_surface = pygame.transform.scale(self.surface, bf.const.RESOLUTION)
253
+ surface.blit(scaled_surface, (0, 0), special_flags=self.blit_special_flags)