feather-engine 26.8.2__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.
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: feather-engine
3
+ Version: 26.8.2
4
+ Summary: A pygame utility module
5
+ Author: TheCodingChihuahua
6
+ License-Expression: GPL-3.0-only
7
+ Requires-Python: >=3.14
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pygame>=2.5
10
+
11
+ # Feather Engine
12
+
13
+ Feather is a small Pygame utility module for beginner-friendly 2D games.
14
+
15
+ ## Example
16
+
17
+ ```python
18
+ import feather
19
+
20
+ player = None
21
+
22
+
23
+ def init():
24
+ global player
25
+ player = feather.Sprite("assets/player.png")
26
+
27
+
28
+ def update():
29
+ if feather.key_pressed("ENTER"):
30
+ player.image = "assets/playerdance.png"
31
+ if feather.key_pressed("BACKSPACE"):
32
+ player.image = "assets/playercry.png"
33
+ if feather.key_pressed("ESCAPE"):
34
+ feather.end()
35
+
36
+
37
+ feather.run(init, update)
38
+ ```
39
+
40
+ `update` runs once per frame at up to 60 frames per second. Image paths are resolved relative to the game's current working directory.
41
+
42
+ ## Installation
43
+
44
+ ```powershell
45
+ python -m pip install feather-engine
46
+ ```
47
+
48
+ Then import it in your game with:
49
+
50
+ ```python
51
+ import feather
52
+ ```
@@ -0,0 +1,42 @@
1
+ # Feather Engine
2
+
3
+ Feather is a small Pygame utility module for beginner-friendly 2D games.
4
+
5
+ ## Example
6
+
7
+ ```python
8
+ import feather
9
+
10
+ player = None
11
+
12
+
13
+ def init():
14
+ global player
15
+ player = feather.Sprite("assets/player.png")
16
+
17
+
18
+ def update():
19
+ if feather.key_pressed("ENTER"):
20
+ player.image = "assets/playerdance.png"
21
+ if feather.key_pressed("BACKSPACE"):
22
+ player.image = "assets/playercry.png"
23
+ if feather.key_pressed("ESCAPE"):
24
+ feather.end()
25
+
26
+
27
+ feather.run(init, update)
28
+ ```
29
+
30
+ `update` runs once per frame at up to 60 frames per second. Image paths are resolved relative to the game's current working directory.
31
+
32
+ ## Installation
33
+
34
+ ```powershell
35
+ python -m pip install feather-engine
36
+ ```
37
+
38
+ Then import it in your game with:
39
+
40
+ ```python
41
+ import feather
42
+ ```
@@ -0,0 +1,399 @@
1
+ import os
2
+ import pygame
3
+
4
+ _running = True
5
+ _objects = []
6
+ _background_color = (0, 0, 0)
7
+
8
+
9
+ def _normalize_layer(value):
10
+ try:
11
+ return int(value)
12
+ except (TypeError, ValueError):
13
+ return 0
14
+
15
+
16
+ def _sort_objects():
17
+ _objects.sort(key=lambda obj: _normalize_layer(getattr(obj, 'layer', 0)), reverse=True)
18
+
19
+ _keys_down = set()
20
+ _key_aliases = {
21
+ 'ENTER': 'RETURN',
22
+ 'ESC': 'ESCAPE',
23
+ 'CTRL': 'CONTROL',
24
+ 'CMD': 'GUI',
25
+ }
26
+
27
+ framerate: int = 60
28
+ def setWindowTitle(title): pygame.display.set_caption(title)
29
+ def setWindowSize(width, height): pygame.display.set_mode((width, height), pygame.RESIZABLE)
30
+ def setBackgroundColor(color):
31
+ global _background_color
32
+ if isinstance(color, str):
33
+ color = pygame.Color(color)
34
+ _background_color = color
35
+ surface = pygame.display.get_surface()
36
+ if surface is not None:
37
+ surface.fill(color)
38
+
39
+ def _key_name(key):
40
+ name = str(key).upper()
41
+ return _key_aliases.get(name, name)
42
+
43
+
44
+ def run(_init, _update):
45
+ global _running
46
+ _running = True
47
+
48
+ if not pygame.get_init():
49
+ pygame.init()
50
+
51
+ if pygame.display.get_surface() is None:
52
+ pygame.display.set_mode((800, 600), pygame.RESIZABLE)
53
+
54
+ clock = pygame.time.Clock()
55
+
56
+ if _init is not None:
57
+ _init()
58
+
59
+ while _running:
60
+ for event in pygame.event.get():
61
+ if event.type == pygame.QUIT:
62
+ end()
63
+ elif event.type == pygame.KEYDOWN:
64
+ _keys_down.add(_key_name(pygame.key.name(event.key)))
65
+ elif event.type == pygame.KEYUP:
66
+ _keys_down.discard(_key_name(pygame.key.name(event.key)))
67
+
68
+ screen = pygame.display.get_surface()
69
+ screen.fill(_background_color)
70
+ _update()
71
+
72
+ for obj in _objects:
73
+ if isinstance(obj, Sprite):
74
+ screen.blit(obj.image, obj.rect)
75
+ elif isinstance(obj, Label):
76
+ screen.blit(obj.image, obj.rect)
77
+ elif isinstance(obj, Rectangle):
78
+ pygame.draw.rect(screen, obj.color, obj.rect)
79
+
80
+ pygame.display.flip()
81
+ clock.tick(framerate)
82
+
83
+
84
+ def end():
85
+ global _running
86
+ _running = False
87
+
88
+ def key_pressed(key):
89
+ return _key_name(key) in _keys_down
90
+
91
+ def add_sprite(sprite):
92
+ if sprite not in _objects:
93
+ _objects.append(sprite)
94
+ _sort_objects()
95
+
96
+ def add_rect(rect):
97
+ if rect not in _objects:
98
+ _objects.append(rect)
99
+ _sort_objects()
100
+
101
+ def add_label(label):
102
+ if label not in _objects:
103
+ _objects.append(label)
104
+ _sort_objects()
105
+
106
+
107
+ class Sprite:
108
+ def __init__(self, image_path, x=0, y=0, width=None, height=None, layer=0, flip_x=False, flip_y=False):
109
+ file_extension = os.path.splitext(image_path)[1].lower().lstrip('.')
110
+
111
+ if file_extension in {'png', 'svg'}:
112
+ self.image = pygame.image.load(image_path).convert_alpha()
113
+ elif file_extension in {'jpg', 'jpeg'}:
114
+ self.image = pygame.image.load(image_path).convert()
115
+ else:
116
+ raise ValueError("Unsupported image format: {}".format(file_extension))
117
+
118
+ self.rect = self.image.get_rect(topleft=(x, y))
119
+
120
+ if width is not None or height is not None:
121
+ target_width = self.rect.width if width is None else width
122
+ target_height = self.rect.height if height is None else height
123
+ self._resize(target_width, target_height)
124
+
125
+ self._layer = _normalize_layer(layer)
126
+ self._flip_x = False
127
+ self._flip_y = False
128
+ self.flip_x = flip_x
129
+ self.flip_y = flip_y
130
+ add_sprite(self)
131
+
132
+ @property
133
+ def x(self):
134
+ return self.rect.x
135
+
136
+ @x.setter
137
+ def x(self, value):
138
+ self.rect.x = value
139
+
140
+ @property
141
+ def y(self):
142
+ return self.rect.y
143
+
144
+ @y.setter
145
+ def y(self, value):
146
+ self.rect.y = value
147
+
148
+ @property
149
+ def width(self):
150
+ return self.rect.width
151
+
152
+ @width.setter
153
+ def width(self, value):
154
+ self._resize(value, self.height)
155
+
156
+ @property
157
+ def height(self):
158
+ return self.rect.height
159
+
160
+ @height.setter
161
+ def height(self, value):
162
+ self._resize(self.width, value)
163
+
164
+ @property
165
+ def layer(self):
166
+ return self._layer
167
+
168
+ @layer.setter
169
+ def layer(self, value):
170
+ self._layer = _normalize_layer(value)
171
+ _sort_objects()
172
+
173
+ def _resize(self, width, height):
174
+ width = max(1, min(int(width), 8192))
175
+ height = max(1, min(int(height), 8192))
176
+ position = self.rect.topleft
177
+ self.image = pygame.transform.scale(self.image, (width, height))
178
+ self.rect = self.image.get_rect(topleft=position)
179
+
180
+ @property
181
+ def flip_x(self):
182
+ return self._flip_x
183
+
184
+ @flip_x.setter
185
+ def flip_x(self, value):
186
+ value = bool(value)
187
+ if value != self._flip_x:
188
+ self.image = pygame.transform.flip(self.image, True, False)
189
+ self._flip_x = value
190
+
191
+ @property
192
+ def flip_y(self):
193
+ return self._flip_y
194
+
195
+ @flip_y.setter
196
+ def flip_y(self, value):
197
+ value = bool(value)
198
+ if value != self._flip_y:
199
+ self.image = pygame.transform.flip(self.image, False, True)
200
+ self._flip_y = value
201
+
202
+ @property
203
+ def image(self):
204
+ return self._image
205
+
206
+ @image.setter
207
+ def image(self, value):
208
+ preserve_size = isinstance(value, str) and hasattr(self, 'rect')
209
+ position = self.rect.topleft if hasattr(self, 'rect') else (0, 0)
210
+
211
+ if isinstance(value, str):
212
+ file_extension = os.path.splitext(value)[1].lower().lstrip('.')
213
+
214
+ if file_extension in {'png', 'svg'}:
215
+ loaded_image = pygame.image.load(value).convert_alpha()
216
+ elif file_extension in {'jpg', 'jpeg'}:
217
+ loaded_image = pygame.image.load(value).convert()
218
+ else:
219
+ raise ValueError("Unsupported image format: {}".format(file_extension))
220
+ elif isinstance(value, pygame.Surface):
221
+ loaded_image = value
222
+ else:
223
+ raise ValueError("Unsupported image type: {}".format(type(value)))
224
+
225
+ if preserve_size:
226
+ loaded_image = pygame.transform.scale(loaded_image, self.rect.size)
227
+ loaded_image = pygame.transform.flip(
228
+ loaded_image,
229
+ getattr(self, '_flip_x', False),
230
+ getattr(self, '_flip_y', False),
231
+ )
232
+
233
+ self._image = loaded_image
234
+ self.rect = self.image.get_rect(topleft=position)
235
+
236
+ class Label:
237
+ def __init__(self, text='', x=0, y=0, layer=0, size=36, color=('black'), font_name=None, rounded=True):
238
+ self._x = x
239
+ self._y = y
240
+ self._text = str(text)
241
+ self._layer = _normalize_layer(layer)
242
+ self._size = size
243
+ self._color = color
244
+ self._font_name = font_name
245
+ self._rounded = rounded
246
+ self.font = pygame.font.Font(self._font_name, self._size)
247
+ self._render()
248
+ add_label(self)
249
+
250
+ def _render(self):
251
+ self.image = self.font.render(self._text, self._rounded, self._color)
252
+ self.rect = self.image.get_rect(topleft=(self._x, self._y))
253
+
254
+ @property
255
+ def x(self):
256
+ return self._x
257
+
258
+ @x.setter
259
+ def x(self, value):
260
+ self._x = value
261
+ if hasattr(self, 'rect'):
262
+ self.rect.x = value
263
+
264
+ @property
265
+ def y(self):
266
+ return self._y
267
+
268
+ @y.setter
269
+ def y(self, value):
270
+ self._y = value
271
+ if hasattr(self, 'rect'):
272
+ self.rect.y = value
273
+
274
+ @property
275
+ def layer(self):
276
+ return self._layer
277
+
278
+ @layer.setter
279
+ def layer(self, value):
280
+ self._layer = _normalize_layer(value)
281
+ _sort_objects()
282
+
283
+ @property
284
+ def text(self):
285
+ return self._text
286
+
287
+ @text.setter
288
+ def text(self, value):
289
+ self._text = str(value)
290
+ self._render()
291
+
292
+ @property
293
+ def size(self):
294
+ return self._size
295
+
296
+ @size.setter
297
+ def size(self, value):
298
+ self._size = value
299
+ self.font = pygame.font.Font(self._font_name, self._size)
300
+ self._render()
301
+
302
+ @property
303
+ def color(self):
304
+ return self._color
305
+
306
+ @color.setter
307
+ def color(self, value):
308
+ self._color = value
309
+ self._render()
310
+
311
+ @property
312
+ def font_name(self):
313
+ return self._font_name
314
+
315
+ @font_name.setter
316
+ def font_name(self, value):
317
+ self._font_name = value
318
+ self.font = pygame.font.Font(self._font_name, self._size)
319
+ self._render()
320
+
321
+ @property
322
+ def rounded(self):
323
+ return self._rounded
324
+
325
+ @rounded.setter
326
+ def rounded(self, value):
327
+ self._rounded = value
328
+ self._render()
329
+
330
+ class Rectangle:
331
+ def __init__(self, x=0, y=0, width=30, height=30, color=(255, 255, 255), layer=0):
332
+ self._x = x
333
+ self._y = y
334
+ self._width = width
335
+ self._height = height
336
+ self.rect = pygame.Rect(x, y, width, height)
337
+ self.color = color
338
+ self._layer = _normalize_layer(layer)
339
+ add_rect(self)
340
+
341
+ @property
342
+ def x(self):
343
+ return self._x
344
+
345
+ @x.setter
346
+ def x(self, value):
347
+ self._x = value
348
+ if hasattr(self, 'rect'):
349
+ self.rect.x = value
350
+
351
+ @property
352
+ def y(self):
353
+ return self._y
354
+
355
+ @y.setter
356
+ def y(self, value):
357
+ self._y = value
358
+ if hasattr(self, 'rect'):
359
+ self.rect.y = value
360
+
361
+ @property
362
+ def width(self):
363
+ return self._width
364
+
365
+ @width.setter
366
+ def width(self, value):
367
+ self._width = value
368
+ if hasattr(self, 'rect'):
369
+ self.rect.width = value
370
+
371
+ @property
372
+ def height(self):
373
+ return self._height
374
+
375
+ @height.setter
376
+ def height(self, value):
377
+ self._height = value
378
+ if hasattr(self, 'rect'):
379
+ self.rect.height = value
380
+
381
+ @property
382
+ def color(self):
383
+ return self._color
384
+
385
+ @color.setter
386
+ def color(self, value):
387
+ self._color = value
388
+
389
+ @property
390
+ def layer(self):
391
+ return self._layer
392
+
393
+ @layer.setter
394
+ def layer(self, value):
395
+ try:
396
+ self._layer = int(value)
397
+ except (TypeError, ValueError):
398
+ self._layer = 0
399
+ _sort_objects()
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: feather-engine
3
+ Version: 26.8.2
4
+ Summary: A pygame utility module
5
+ Author: TheCodingChihuahua
6
+ License-Expression: GPL-3.0-only
7
+ Requires-Python: >=3.14
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pygame>=2.5
10
+
11
+ # Feather Engine
12
+
13
+ Feather is a small Pygame utility module for beginner-friendly 2D games.
14
+
15
+ ## Example
16
+
17
+ ```python
18
+ import feather
19
+
20
+ player = None
21
+
22
+
23
+ def init():
24
+ global player
25
+ player = feather.Sprite("assets/player.png")
26
+
27
+
28
+ def update():
29
+ if feather.key_pressed("ENTER"):
30
+ player.image = "assets/playerdance.png"
31
+ if feather.key_pressed("BACKSPACE"):
32
+ player.image = "assets/playercry.png"
33
+ if feather.key_pressed("ESCAPE"):
34
+ feather.end()
35
+
36
+
37
+ feather.run(init, update)
38
+ ```
39
+
40
+ `update` runs once per frame at up to 60 frames per second. Image paths are resolved relative to the game's current working directory.
41
+
42
+ ## Installation
43
+
44
+ ```powershell
45
+ python -m pip install feather-engine
46
+ ```
47
+
48
+ Then import it in your game with:
49
+
50
+ ```python
51
+ import feather
52
+ ```
@@ -0,0 +1,8 @@
1
+ README.md
2
+ feather.py
3
+ pyproject.toml
4
+ feather_engine.egg-info/PKG-INFO
5
+ feather_engine.egg-info/SOURCES.txt
6
+ feather_engine.egg-info/dependency_links.txt
7
+ feather_engine.egg-info/requires.txt
8
+ feather_engine.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ pygame>=2.5
@@ -0,0 +1,16 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "feather-engine"
7
+ version = "26.8.2"
8
+ description = "A pygame utility module"
9
+ readme = "README.md"
10
+ requires-python = ">=3.14"
11
+ authors = [{ name = "TheCodingChihuahua" }]
12
+ license = "GPL-3.0-only"
13
+ dependencies = ["pygame>=2.5"]
14
+
15
+ [tool.setuptools]
16
+ py-modules = ["feather"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+