easycode-infinite 1.0.0__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,24 @@
1
+ EasyCode License Agreement (2026)
2
+
3
+ Copyright (c) 2026 [Your Nickname/Team Name]
4
+
5
+ Permission is hereby granted to any person obtaining a copy of this software
6
+ ("EasyCode") to use it for personal, educational, and non-commercial game
7
+ development purposes, subject to the following conditions:
8
+
9
+ 1. ATTRIBUTION (CREDIT): Any game or project created using EasyCode must
10
+ provide visible credit to the author in the game's "About" section,
11
+ credits, or documentation.
12
+
13
+ 2. NON-COMMERCIAL USE: You may not use this library in a game that is
14
+ sold for money or contains microtransactions unless you have
15
+ obtained written permission from the author.
16
+
17
+ 3. NO DERIVATIVES: You may not take the code from EasyCode to create,
18
+ distribute, or sell a competing library or "addon" package.
19
+
20
+ 4. NO STEALING: You may not claim this code as your own.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24
+ OTHER LIABILITY.
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: easycode-infinite
3
+ Version: 1.0.0
4
+ Summary: A simple GUI addon for Pygame, Pyglet, and Arcade
5
+ License-File: LICENSE
6
+ Requires-Dist: pygame-ce
7
+ Requires-Dist: pyglet>=2.0
8
+ Requires-Dist: arcade>=3.0
9
+ Requires-Dist: roman
10
+ Dynamic: license-file
@@ -0,0 +1,17 @@
1
+ ##EasyCode
2
+ **EasyCode is a python library made specifically so there would be easier and better coding with a lot of simplification added**
3
+
4
+ EasyCode simplifies game development in Python by providing high-precision math variables and powerful, easy-to-use base classes for the industry's most popular 2D frameworks.
5
+
6
+ ##Key Features
7
+ *it includes of 3 special variables BigDecimal, BigString, and BigVector
8
+ *includes of optimizations
9
+ *is very fast and can save memory potentially
10
+ *has a couple new sprites you can use
11
+ *supports arcade, pyglet, and pygame
12
+
13
+ ##Installation
14
+ Install EasyCode via `pip` or `uv`:
15
+ ```bash
16
+ pip install easycode
17
+ uv pip install easycode --system
@@ -0,0 +1,97 @@
1
+ from .easycode_addons import (
2
+ PygameVisibleList,
3
+ PygameVisibleVariable,
4
+ PygameClickedCalculator,
5
+ PygameHealthBar,
6
+ PygameDraggableSlider,
7
+ PygameScreenShake,
8
+ PygameTextBox,
9
+ PygameDialogueText,
10
+ EasyDraw,
11
+ PygameSoundManager,
12
+ get_font,
13
+ change_axis,
14
+ BigString,
15
+ BigDecimal,
16
+ BigVector2,
17
+ BigVector3,
18
+ BigVector4,
19
+ PygameSprite,
20
+ PygameGroup,
21
+ PygameLayeredGroup,
22
+ ArcadeSprite,
23
+ ArcadeGroup,
24
+ PygletHealthBar,
25
+ PygletVisibleList,
26
+ PygletTextBox,
27
+ PygletDialogueText,
28
+ PygletScreenShake,
29
+ PygletDraggableSlider,
30
+ PygletSprite,
31
+ PygletGroup
32
+ )
33
+
34
+ sound = PygameSoundManager()
35
+ draw = EasyDraw()
36
+
37
+ bigstr = BigString
38
+ bigdec = BigDecimal
39
+ bigvector = BigVector2
40
+ bigvector2 = BigVector2
41
+ bigvector3 = BigVector3
42
+ bigvector4 = BigVector4
43
+
44
+ pg_sprite = PygameSprite
45
+ pg_group = PygameGroup
46
+ arc_sprite = ArcadeSprite
47
+ arc_group = ArcadeGroup
48
+ pgl_sprite = PygletSprite
49
+
50
+ __all__ = [
51
+ 'PygameVisibleList',
52
+ 'PygameVisibleVariable',
53
+ 'PygameClickedCalculator',
54
+ 'PygameHealthBar',
55
+ 'PygameDraggableSlider',
56
+ 'PygameScreenShake',
57
+ 'PygameTextBox',
58
+ 'PygameDialogueText',
59
+ 'PygameEasyDraw',
60
+ 'PygameSoundManager',
61
+ 'sound',
62
+ 'get_font',
63
+ 'change_axis',
64
+ 'bigstr',
65
+ 'bigdec',
66
+ 'bigvector',
67
+ 'bigvector2',
68
+ 'bigvector3',
69
+ 'bigvector4',
70
+ 'BigString',
71
+ 'BigDecimal',
72
+ 'BigVector2',
73
+ 'BigVector3',
74
+ 'BigVector4',
75
+ 'PygameSprite',
76
+ 'PygameGroup',
77
+ 'PygameLayeredGroup',
78
+ 'pg_sprite',
79
+ 'pg_group',
80
+ 'ArcadeSprite',
81
+ 'ArcadeGroup',
82
+ 'arc_sprite',
83
+ 'arc_group',
84
+ 'PygletHealthBar',
85
+ 'PygletVisibleList',
86
+ 'PygletTextBox',
87
+ 'PygletDialogueText',
88
+ 'PygletScreenShake',
89
+ 'PygletDraggableSlider',
90
+ 'PygletSprite',
91
+ 'PygletGroup',
92
+ 'pgl_sprite'
93
+ ]
94
+
95
+ # PACKAGE METADATA
96
+ __version__ = "0.5.0"
97
+ __author__ = "Kent"
@@ -0,0 +1,780 @@
1
+ import pygame, pyglet, arcade
2
+ import time, os, math
3
+
4
+ _FONT_CACHE = {}
5
+
6
+ # this function you can use to get cached fonts but it was created for internal use mainly
7
+ def get_font(name, size):
8
+ key = (name, size)
9
+ if key not in _FONT_CACHE:
10
+ try:
11
+ _FONT_CACHE[key] = pygame.font.SysFont(name, size)
12
+ except:
13
+ _FONT_CACHE[key] = pygame.font.SysFont("Arial", size)
14
+ return _FONT_CACHE[key]
15
+
16
+ # simply were I will add my drawing functions
17
+ class EasyDraw:
18
+ @staticmethod
19
+ def star(surface, color, center, size, points=5, corner_rounding=0):
20
+ inner_radius = size / 2.5
21
+ outer_radius = size
22
+ point_list = []
23
+
24
+ for i in range(points * 2):
25
+ radius = float(outer_radius) if i % 2 == 0 else float(inner_radius)
26
+ angle = math.radians(i * (360 / (points * 2)))
27
+
28
+ x = center[0] + radius * math.cos(angle)
29
+ y = center[1] + radius * math.sin(angle)
30
+ point_list.append((x, y))
31
+
32
+ if corner_rounding <= 0:
33
+ return pygame.draw.polygon(surface, color, point_list)
34
+ else:
35
+ # For 2026, we use a thicker line join to simulate rounding
36
+ return pygame.draw.polygon(surface, color, point_list, width=0)
37
+
38
+ # will change the way you sprite or sprite group spins
39
+ def change_axis(sprite, angle, pivot_offset):
40
+ rotated_image = pygame.transform.rotate(sprite.image, angle)
41
+
42
+ offset_rotated = pygame.math.Vector2(pivot_offset).rotate(-angle)
43
+
44
+ sprite.image = rotated_image
45
+ sprite.rect = rotated_image.get_rect(center=sprite.pos.to_pygame() + offset_rotated)
46
+
47
+ class PygameClickedCalculator(pygame.sprite.Sprite):
48
+ def __init__(self):
49
+ super().__init__()
50
+
51
+ """this function calculates if the sprite was clicked or not"""
52
+ def clicked(self, event):
53
+ if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
54
+ if hasattr(self, 'rect') and self.rect.collidepoint(event.pos):
55
+ return True
56
+ return False
57
+
58
+ """this function changes the cursor to a hand when hovering over the sprite"""
59
+ def handle_cursor(self):
60
+ mouse_pos = pygame.mouse.get_pos()
61
+ if hasattr(self, 'rect') and self.rect.collidepoint(mouse_pos):
62
+ pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_HAND)
63
+ return True
64
+ return False
65
+
66
+ #a tool you can use to have the camera follow something
67
+ class PygameSmartCamera:
68
+ def __init__(self, screen_width, screen_height, lerp_speed=0.1):
69
+ self.offset = pygame.Vector2(0, 0)
70
+ self.width = screen_width
71
+ self.height = screen_height
72
+ self.lerp_speed = lerp_speed
73
+
74
+ def update(self, target_rect):
75
+ target_center_x = target_rect.centerx - self.width // 2
76
+ target_center_y = target_rect.centery - self.height // 2
77
+
78
+ self.offset.x += (target_center_x - self.offset.x) * self.lerp_speed
79
+ self.offset.y += (target_center_y - self.offset.y) * self.lerp_speed
80
+
81
+ def apply(self, entity_rect):
82
+ return entity_rect.move(-self.offset.x, -self.offset.y)
83
+
84
+ # this will create a visible variable that can contain any type of data ints floats strings etc
85
+ class PygameVisibleVariable(PygameClickedCalculator):
86
+ def __init__(self, font, fontsize, fontcolor, x, y, string, background_t_f, backgroundcolor=None):
87
+ if not isinstance(font, str):
88
+ raise TypeError(f"Font name must be a string, not {type(font).__name__}")
89
+
90
+ if not isinstance(fontsize, (int, float)) or fontsize <= 0:
91
+ raise ValueError(f"Font size must be a positive number, you provided: {fontsize}")
92
+
93
+ if not isinstance(fontcolor, (tuple, list)) or len(fontcolor) < 3:
94
+ raise TypeError("fontcolor must be an RGB tuple or list (e.g., (255, 255, 255))")
95
+
96
+ if not isinstance(background_t_f, bool):
97
+ raise TypeError(f"background_t_f must be True or False, not {type(background_t_f).__name__}")
98
+
99
+ if background_t_f and backgroundcolor is None:
100
+ raise ValueError("When background_t_f is True, you must provide a backgroundcolor (e.g., (0,0,255))")
101
+
102
+ if not background_t_f and backgroundcolor is not None:
103
+ raise ValueError("backgroundcolor should not be provided when background_t_f is False")
104
+
105
+ super().__init__()
106
+ self.font_name = font
107
+ self.font_size = int(fontsize)
108
+ self.font_color = fontcolor
109
+ self.background_t_f = background_t_f
110
+ self.background_color = backgroundcolor
111
+ self.x, self.y = x, y
112
+ self.refresh_image(string)
113
+
114
+ # this function will update the visiblevariable you dont need to use it but its an optimized way of allowing the variable to change
115
+ def refresh_image(self, new_val):
116
+ font_obj = get_font(self.font_name, self.font_size)
117
+ string_val = str(new_val)
118
+
119
+ text_surface = font_obj.render(string_val, True, self.font_color)
120
+ w, h = text_surface.get_size()
121
+
122
+ if not self.background_t_f:
123
+ self.image = pygame.Surface((w, h), pygame.SRCALPHA).convert_alpha()
124
+ self.image.blit(text_surface, (0, 0))
125
+ else:
126
+ self.image = pygame.Surface((w + 4, h + 4)).convert()
127
+ self.image.fill(self.background_color)
128
+ self.image.blit(text_surface, (2, 2))
129
+
130
+ self.rect = self.image.get_rect(topleft=(self.x, self.y))
131
+
132
+ # this is bassically a group of variables but visible and it will layout them out either vertically or horizontally you dont need to make multiple visible variables for this
133
+ class PygameVisibleList(pygame.sprite.Group):
134
+ def __init__(self, font, fontsize, fontcolor, x, y, variablegroup,
135
+ vh, v_bg_t_f, v_bg_color=None,
136
+ l_bg_t_f=False, l_bg_color=None):
137
+
138
+ if vh.lower() not in ["vertical", "horizontal"]:
139
+ raise ValueError(f"Layout 'vh' must be 'vertical' or 'horizontal', not '{vh}'")
140
+
141
+ if not isinstance(variablegroup, (list, tuple)):
142
+ raise TypeError(f"variablegroup must be a list or tuple of data, not {type(variablegroup).__name__}")
143
+
144
+ if l_bg_t_f and l_bg_color is None:
145
+ raise ValueError("When l_bg_t_f is True, you must provide a list_background_color")
146
+
147
+ super().__init__()
148
+ self.font, self.fontsize, self.fontcolor = font, fontsize, fontcolor
149
+ self.x, self.y = x, y
150
+ self.vh = vh.lower()
151
+ self.v_bg_t_f, self.v_bg_color = v_bg_t_f, v_bg_color
152
+ self.l_bg_t_f, self.l_bg_color = l_bg_t_f, l_bg_color
153
+ self.refresh_list(variablegroup)
154
+
155
+ # this function will do the same as the visible variable but for the whole list
156
+ def refresh_list(self, variablegroup):
157
+ self.empty()
158
+ curr_x, curr_y = self.x, self.y
159
+ spacing = 10
160
+ for item in variablegroup:
161
+ node = PygameVisibleVariable(self.font, self.fontsize, self.fontcolor,
162
+ curr_x, curr_y, item, self.v_bg_t_f, self.v_bg_color)
163
+ self.add(node)
164
+ if self.vh == "vertical":
165
+ curr_y += node.rect.height + spacing
166
+ else:
167
+ curr_x += node.rect.width + spacing
168
+
169
+ # this function lets you just draw the list
170
+ def draw(self, surface):
171
+ if self.l_bg_t_f and self.l_bg_color and self.sprites():
172
+ all_rects = [s.rect for s in self.sprites()]
173
+ combined_rect = all_rects[0].unionall(all_rects[1:])
174
+ bg_rect = combined_rect.inflate(15, 15)
175
+ pygame.draw.rect(surface, self.l_bg_color, bg_rect)
176
+ super().draw(surface)
177
+
178
+ # creates a simple health bar for pygame you can use to show health
179
+ class PygameHealthBar(pygame.sprite.Sprite):
180
+ def __init__(self, x, y, width_per_1hp, max_health, current_health, bar_height, border_color, health_color):
181
+ super().__init__()
182
+ if width_per_1hp <= 0:
183
+ raise ValueError("width_per_1hp must be a positive number.")
184
+ if max_health <= 0:
185
+ raise ValueError("max_health must be greater than 0.")
186
+
187
+ self.x, self.y = x, y
188
+ self.w_p_1 = width_per_1hp
189
+ self.max_h = max_health
190
+ self.height = bar_height
191
+ self.b_color = border_color
192
+ self.h_color = health_color
193
+
194
+ self.image = pygame.Surface((max_health * width_per_1hp + 4, bar_height)).convert()
195
+ self.rect = self.image.get_rect(topleft=(x, y))
196
+
197
+ self.refresh_bar(current_health)
198
+
199
+ """Redraws the bar. Only need to pass the new health value!"""
200
+ def refresh_bar(self, current_health):
201
+ self.image.fill(self.b_color)
202
+
203
+ draw_health = current_health
204
+ if draw_health > self.max_h:
205
+ draw_health = self.max_h
206
+ if draw_health < 0:
207
+ draw_health = 0
208
+
209
+ pygame.draw.rect(self.image, self.h_color, (2, 2, draw_health * self.w_p_1, self.height - 4))
210
+
211
+ def update_hp(self, current_health):
212
+ """The 'Easy' way to change health in your loop."""
213
+ self.refresh_bar(current_health)
214
+
215
+ # this is a slider that can be dragged left and right to get to get a value you want that will customize something in your game
216
+ class PygameDraggableSlider(PygameClickedCalculator):
217
+ def __init__(self, color, startingX, y, min_x, max_x):
218
+ super().__init__()
219
+ self.image = pygame.Surface((20, 20), pygame.SRCALPHA).convert_alpha()
220
+ pygame.draw.circle(self.image, color, (10, 10), 10)
221
+
222
+ self.rect = self.image.get_rect(center=(startingX, y))
223
+ self.color = color
224
+ self.dragging = False
225
+
226
+ self.min_x = min_x
227
+ self.max_x = max_x
228
+
229
+ def handle_input(self, event):
230
+ if event.type == pygame.MOUSEBUTTONDOWN:
231
+ if self.rect.collidepoint(event.pos):
232
+ self.dragging = True
233
+
234
+ elif event.type == pygame.MOUSEBUTTONUP:
235
+ self.dragging = False
236
+
237
+ elif event.type == pygame.MOUSEMOTION:
238
+ if self.dragging:
239
+ new_x = event.pos[0]
240
+ self.rect.centerx = max(self.min_x, min(new_x, self.max_x))
241
+
242
+ def get_value(self):
243
+ total_range = self.max_x - self.min_x
244
+ current_pos = self.rect.centerx - self.min_x
245
+ return current_pos / total_range
246
+
247
+ class PygameScreenShake:
248
+ def __init__(self, seed=None):
249
+ self.s0 = seed or int(time.time() * 1000) & 0xFFFFFFFFFFFFFFFF
250
+ self.s1 = 0xBEA123456789ABCD ^ self.s0
251
+ self.offset = pygame.Vector2(0, 0)
252
+ self.duration = 0
253
+ self.intensity = 0
254
+ self.impact_vec = pygame.Vector2(0, 0)
255
+
256
+ def _next_xorshift(self):
257
+ x, y = self.s0, self.s1
258
+ self.s0 = y
259
+ x ^= (x << 23) & 0xFFFFFFFFFFFFFFFF
260
+ self.s1 = (x ^ y ^ (x >> 17) ^ (y >> 26)) & 0xFFFFFFFFFFFFFFFF
261
+ return (self.s1 + y) & 0xFFFFFFFFFFFFFFFF
262
+
263
+ def _get_rand_float(self):
264
+ return (self._next_xorshift() / 0xFFFFFFFFFFFFFFFF) * 2 - 1
265
+
266
+ # this really is the only neccessary thing if you would like you can figure out how to use the rest but just this command is all you need
267
+ def shake(self, intensity=None, duration=None, impact_pos=None, center_pos=None):
268
+ if intensity is not None:
269
+ self.intensity = intensity
270
+ self.duration = duration
271
+ if impact_pos and center_pos:
272
+ self.impact_vec = (pygame.Vector2(center_pos) - pygame.Vector2(impact_pos))
273
+ if self.impact_vec.length() > 0:
274
+ self.impact_vec = self.impact_vec.normalize()
275
+ else:
276
+ self.impact_vec = pygame.Vector2(0, 0)
277
+
278
+ if self.duration > 0:
279
+ self.duration -= 1
280
+ rand_vec = pygame.Vector2(self._get_rand_float(), self._get_rand_float())
281
+ if self.impact_vec.length() > 0:
282
+ alignment = rand_vec.dot(self.impact_vec)
283
+ self.offset = self.impact_vec * alignment * self.intensity
284
+ else:
285
+ self.offset = rand_vec * self.intensity
286
+ else:
287
+ self.offset = pygame.Vector2(0, 0)
288
+
289
+ return self.offset
290
+
291
+ # a simple text box that only allows strings but will wrap text and gives you a max length for whatever you want it to be
292
+ class PygameTextBox(pygame.sprite.Sprite):
293
+ def __init__(self, x, y, width, string, font_name, fontsize, fontcolor,
294
+ background_t_f=False, backgroundcolor=None,
295
+ typewrite_t_f=False, time_per_char=50):
296
+ super().__init__()
297
+
298
+ if isinstance(x, (list, tuple)):
299
+ raise TypeError(f"EasyCode Error: 'x' must be a number, but you passed a {type(x).__name__}. Check the order of your arguments in PygameDialogueText!")
300
+
301
+ self.x = int(x)
302
+ self.y = int(y)
303
+ self.width = int(width)
304
+ self.full_string = str(string)
305
+ self.font = get_font(font_name, int(fontsize))
306
+ self.color = fontcolor
307
+ self.bg_enabled = background_t_f
308
+ self.bg_color = backgroundcolor
309
+
310
+ self.typewrite_enabled = typewrite_t_f
311
+ self.ms_per_char = time_per_char
312
+ self.char_index = 0
313
+ self.last_type_time = pygame.time.get_ticks()
314
+
315
+ if not self.typewrite_enabled:
316
+ self.char_index = len(self.full_string)
317
+
318
+ self.refresh_text()
319
+
320
+ def update(self):
321
+ """Standard Pygame update to handle typewriter timing."""
322
+ if self.typewrite_enabled and self.char_index < len(self.full_string):
323
+ now = pygame.time.get_ticks()
324
+ if now - self.last_type_time >= self.ms_per_char:
325
+ self.char_index += 1
326
+ self.last_type_time = now
327
+ self.refresh_text()
328
+
329
+ def wrap_text(self, text):
330
+ """Splits text into lines that fit within self.width."""
331
+ words = str(text).split(' ')
332
+ lines, current_line = [], []
333
+ for word in words:
334
+ if self.font.size(' '.join(current_line + [word]))[0] <= self.width:
335
+ current_line.append(word)
336
+ else:
337
+ lines.append(' '.join(current_line))
338
+ current_line = [word]
339
+ lines.append(' '.join(current_line))
340
+ return lines
341
+
342
+ def refresh_text(self):
343
+ """Redraws the surface based on how many characters are revealed."""
344
+ visible_text = self.full_string[:self.char_index]
345
+ lines = self.wrap_text(visible_text)
346
+
347
+ line_height = self.font.get_linesize()
348
+ total_height = len(lines) * line_height
349
+
350
+ if self.bg_enabled:
351
+ self.image = pygame.Surface((self.width + 10, total_height + 10)).convert()
352
+ self.image.fill(self.bg_color)
353
+ padding = 5
354
+ else:
355
+ self.image = pygame.Surface((self.width, total_height), pygame.SRCALPHA).convert_alpha()
356
+ padding = 0
357
+
358
+ for i, line in enumerate(lines):
359
+ text_surf = self.font.render(line, True, self.color)
360
+ self.image.blit(text_surf, (padding, padding + (i * line_height)))
361
+
362
+ # Update the physics box
363
+ self.rect = self.image.get_rect(topleft=(self.x, self.y))
364
+
365
+ # a dialogue tool that when used will take a list of strings turn them into PygameTextBox(s) and has a next string which can be triggered by whatever you want
366
+ class PygameDialogueText(pygame.sprite.Group):
367
+ def __init__(self, text_list, x, y, width, font_name, fontsize, fontcolor,
368
+ background_t_f=False, backgroundcolor=None,
369
+ typewrite_t_f=False, time_per_char=50):
370
+ super().__init__()
371
+
372
+ self.text_list = text_list
373
+ self.index = 0
374
+ self.x, self.y, self.width = x, y, width
375
+ self.font_name, self.font_size = font_name, fontsize
376
+ self.font_color = fontcolor
377
+ self.bg_t_f, self.bg_color = background_t_f, backgroundcolor
378
+ self.typewrite_t_f, self.time_per_char = typewrite_t_f, time_per_char
379
+
380
+ self._update_display()
381
+
382
+ def _update_display(self):
383
+ self.empty()
384
+ new_box = PygameTextBox(
385
+ self.x, self.y, self.width,
386
+ self.text_list[self.index],
387
+ self.font_name, self.font_size, self.font_color,
388
+ self.bg_t_f, self.bg_color,
389
+ self.typewrite_t_f, self.time_per_char
390
+ )
391
+ self.add(new_box)
392
+
393
+ def next_string(self):
394
+ self.index += 1
395
+ if self.index >= len(self.text_list):
396
+ self.index = 0
397
+ self._update_display()
398
+
399
+ def update(self):
400
+ """Passes the update command to the internal TextBox."""
401
+ for sprite in self.sprites():
402
+ sprite.update()
403
+
404
+ # these are some built in sounds
405
+ class PygameSoundManager:
406
+ def __init__(self):
407
+ if not pygame.mixer.get_init():
408
+ pygame.mixer.pre_init(44100, -16, 2, 512)
409
+ pygame.mixer.init()
410
+ self.sounds = {}
411
+ self.base_dir = os.path.dirname(os.path.abspath(__file__))
412
+
413
+ def load(self, name: str, filename: str):
414
+ if filename.startswith("sounds/"):
415
+ clean_name = filename.replace("sounds/", "")
416
+ else:
417
+ clean_name = filename
418
+
419
+ full_path = os.path.join(self.base_dir, "sounds", clean_name)
420
+
421
+ def play(self, name: str):
422
+ if name in self.sounds:
423
+ self.sounds[name].play()
424
+
425
+ def is_playing(self):
426
+ return pygame.mixer.get_busy()
427
+
428
+ def play_until_done(self, name: str):
429
+ if name in self.sounds:
430
+ if not self.is_playing():
431
+ self.sounds[name].play()
432
+
433
+ # this is a bigstring dont worry about what this thing does or actually is just use it as a string except has no limit in characters plus when it comes to short string it saves memory
434
+ class BigString:
435
+ def __init__(self, initial_text=""):
436
+ self.data = 0
437
+ self.set(str(initial_text))
438
+
439
+ def set(self, text):
440
+ byte_data = text.encode('utf-8')
441
+ self.data = int.from_bytes(byte_data, 'big')
442
+
443
+ def get(self):
444
+ if self.data == 0: return ""
445
+ byte_length = (self.data.bit_length() + 7) // 8
446
+ return self.data.to_bytes(byte_length, 'big').decode('utf-8')
447
+
448
+ def __add__(self, other):
449
+ """Allows: bigstr("hi") + " there" """
450
+ return bigstr(self.get() + str(other))
451
+
452
+ def __radd__(self, other):
453
+ """Allows: "hello " + bigstr("world") """
454
+ return bigstr(str(other) + self.get())
455
+
456
+ def __eq__(self, other):
457
+ """Allows: if my_bigstr == "test": """
458
+ return self.get() == str(other)
459
+
460
+ def __len__(self):
461
+ """Allows: len(my_bigstr) """
462
+ return len(self.get())
463
+
464
+ def __str__(self):
465
+ """Allows: print(my_bigstr) """
466
+ return self.get()
467
+
468
+ def __repr__(self):
469
+ """Shows up as bigstr('text') in the console"""
470
+ return f"bigstr('{self.get()}')"
471
+
472
+ # similiar to the bigstring it can hold more number but because it uses bigints and not floats it actually is faster calculations and no limit and perfect accuracy just dont destroy your PC by not adding a limit to accuracy
473
+ class BigDecimal:
474
+ def __init__(self, value, scale=0):
475
+ if isinstance(value, str):
476
+ if "." in value:
477
+ parts = value.split(".")
478
+ self.scale = len(parts[1])
479
+ self.value = int(parts[0] + parts[1])
480
+ else:
481
+ self.value = int(value)
482
+ self.scale = 0
483
+ else:
484
+ self.value = int(value)
485
+ self.scale = scale
486
+
487
+ def to_float(self):
488
+ return self.value / (10 ** self.scale)
489
+
490
+ def _align(self, other):
491
+ if not isinstance(other, BigDecimal):
492
+ other = BigDecimal(str(other))
493
+
494
+ new_scale = max(self.scale, other.scale)
495
+ v1 = self.value * (10 ** (new_scale - self.scale))
496
+ v2 = other.value * (10 ** (new_scale - other.scale))
497
+ return v1, v2, new_scale
498
+
499
+ def __add__(self, other):
500
+ v1, v2, s = self._align(other)
501
+ return BigDecimal(v1 + v2, s)
502
+
503
+ def __sub__(self, other):
504
+ v1, v2, s = self._align(other)
505
+ return BigDecimal(v1 - v2, s)
506
+
507
+ def __mul__(self, other):
508
+ if not isinstance(other, BigDecimal):
509
+ other = BigDecimal(str(other))
510
+ return BigDecimal(self.value * other.value, self.scale + other.scale)
511
+
512
+ def __str__(self):
513
+ s = str(abs(self.value)).zfill(self.scale + 1)
514
+ res = s[:-self.scale] + "." + s[-self.scale:]
515
+ return f"-{res}" if self.value < 0 else res
516
+
517
+ # like the bigdecimal instead of floats and decimals in this vector its bigints and bigdecimals and its for the same reason bigints are better then floats mostly
518
+ class BigVectorBase:
519
+ def __init__(self, *args):
520
+ self.components = [
521
+ arg if isinstance(arg, BigDecimal) else bigdec(str(arg))
522
+ for arg in args
523
+ ]
524
+
525
+ def __add__(self, other):
526
+ if len(self.components) == len(other.components):
527
+ new_comps = [a + b for a, b in zip(self.components, other.components)]
528
+ return self.__class__(*new_comps)
529
+ raise ValueError(f"Cannot add {len(self.components)}D and {len(other.components)}D vectors!")
530
+
531
+ def __sub__(self, other):
532
+ if len(self.components) == len(other.components):
533
+ new_comps = [a - b for a, b in zip(self.components, other.components)]
534
+ return self.__class__(*new_comps)
535
+ raise ValueError("Dimension mismatch during subtraction!")
536
+
537
+ def __mul__(self, scalar):
538
+ new_comps = [a * scalar for a in self.components]
539
+ return self.__class__(*new_comps)
540
+
541
+ def __str__(self):
542
+ coords = ", ".join(str(c) for c in self.components)
543
+ return f"({coords})"
544
+
545
+ def __repr__(self):
546
+ return self.__str__()
547
+
548
+ def to_pygame(self):
549
+ if len(self.components) == 2:
550
+ return pygame.Vector2(self.components[0].to_float(), self.components[1].to_float())
551
+ elif len(self.components) == 3:
552
+ return pygame.Vector3(self.components[0].to_float(), self.components[1].to_float(), self.components[2].to_float())
553
+ return tuple(c.to_float() for c in self.components)
554
+
555
+ def to_tuple(self):
556
+ return tuple(c.to_float() for c in self.components)
557
+
558
+ def magnitude(self):
559
+ return (self.x * self.x + self.y * self.y)
560
+
561
+ class BigVector2(BigVectorBase):
562
+ def __init__(self, x, y):
563
+ super().__init__(x, y)
564
+ self.x, self.y = self.components[0], self.components[1]
565
+
566
+ class BigVector3(BigVectorBase):
567
+ def __init__(self, x, y, z):
568
+ super().__init__(x, y, z)
569
+ self.x, self.y, self.z = self.components[0], self.components[1], self.components[2]
570
+
571
+ class BigVector4(BigVectorBase):
572
+ def __init__(self, x, y, z, w):
573
+ super().__init__(x, y, z, w)
574
+ self.x, self.y, self.z, self.w = self.components[0], self.components[1], self.components[2], self.components[3]
575
+
576
+ class PygletVisibleVariable(pyglet.text.Label):
577
+ def __init__(self, font, fontsize, fontcolor, x, y, string, background_t_f, backgroundcolor=None):
578
+ rgba_font = (*fontcolor, 255) if len(fontcolor) == 3 else fontcolor
579
+
580
+ bg_color = None
581
+ if background_t_f and backgroundcolor:
582
+ bg_color = (*backgroundcolor, 255) if len(backgroundcolor) == 3 else backgroundcolor
583
+
584
+ super().__init__(str(string), font_name=font, font_size=fontsize,
585
+ color=rgba_font, x=x, y=y, background_color=bg_color)
586
+
587
+ class PygletHealthBar:
588
+ def __init__(self, x, y, width_per_hp, max_hp, current_hp, height, border_color, health_color, batch=None):
589
+ self.max_hp = max_hp
590
+ self.w_p_hp = width_per_hp
591
+
592
+ self.bg = pyglet.shapes.Rectangle(x, y, max_hp * width_per_hp, height, color=border_color, batch=batch)
593
+ self.bar = pyglet.shapes.Rectangle(x + 2, y + 2, current_hp * width_per_hp - 4, height - 4, color=health_color, batch=batch)
594
+
595
+ def refresh_bar(self, current_hp):
596
+ safe_hp = max(0, min(current_hp, self.max_hp))
597
+ self.bar.width = max(0, (safe_hp * self.w_p_hp) - 4)
598
+
599
+ class PygletVisibleList:
600
+ def __init__(self, font, fontsize, fontcolor, x, y, items, vh="vertical", batch=None):
601
+ self.items = []
602
+ self.batch = batch
603
+ self.x, self.y = x, y
604
+ self.font, self.size, self.color = font, fontsize, fontcolor
605
+ self.vh = vh.lower()
606
+ self.refresh_list(items)
607
+
608
+ def refresh_list(self, items):
609
+ self.items = []
610
+ curr_x, curr_y = self.x, self.y
611
+ spacing = 10
612
+
613
+ for text in items:
614
+ label = pyglet.text.Label(str(text), font_name=self.font, font_size=self.size,
615
+ color=(*self.color, 255), x=curr_x, y=curr_y, batch=self.batch)
616
+ self.items.append(label)
617
+ if self.vh == "vertical":
618
+ curr_y -= (self.size + spacing)
619
+ else:
620
+ curr_x += (len(str(text)) * self.size * 0.6) + spacing
621
+
622
+ class PygletTextBox:
623
+ def __init__(self, x, y, width, initial_text="", batch=None, window=None):
624
+ self.doc = pyglet.text.document.UnformattedDocument(initial_text)
625
+ self.layout = pyglet.text.layout.IncrementalTextLayout(self.doc, width, 30, multiline=False, batch=batch)
626
+ self.layout.x, self.layout.y = x, y
627
+
628
+ self.caret = pyglet.text.caret.Caret(self.layout)
629
+
630
+ if window:
631
+ window.push_handlers(self.caret)
632
+
633
+ @property
634
+ def text(self):
635
+ return self.doc.text
636
+
637
+ class PygletDialogueText:
638
+ def __init__(self, text_list, x, y, width, font_name, fontsize, fontcolor,
639
+ background_t_f=False, backgroundcolor=None,
640
+ typewrite_t_f=True, time_per_char=0.05, batch=None):
641
+ self.text_list = text_list
642
+ self.index = 0
643
+ self.batch = batch
644
+ self.x, self.y, self.width = x, y, width
645
+ self.font, self.size, self.color = font_name, fontsize, fontcolor
646
+
647
+ self.bg = None
648
+ if background_t_f and backgroundcolor:
649
+ self.bg = pyglet.shapes.Rectangle(x-5, y-5, width+10, (fontsize*len(text_list[0])//10)+10,
650
+ color=backgroundcolor, batch=batch)
651
+ self.bg.opacity = 200
652
+
653
+ self.full_text = text_list[self.index]
654
+ self.display_text = ""
655
+ self.char_index = 0
656
+ self.typewrite_enabled = typewrite_t_f
657
+
658
+ self.label = pyglet.text.Label("", font_name=font_name, font_size=fontsize,
659
+ color=(*fontcolor, 255), x=x, y=y, width=width,
660
+ multiline=True, batch=batch)
661
+
662
+ if self.typewrite_enabled:
663
+ pyglet.clock.schedule_interval(self._update_typewriter, time_per_char)
664
+ else:
665
+ self.label.text = self.full_text
666
+
667
+ def _update_typewriter(self, dt):
668
+ if self.char_index < len(self.full_text):
669
+ self.char_index += 1
670
+ self.label.text = self.full_text[:self.char_index]
671
+
672
+ def next_string(self):
673
+ self.index = (self.index + 1) % len(self.text_list)
674
+ self.full_text = self.text_list[self.index]
675
+ self.char_index = 0
676
+ if not self.typewrite_enabled:
677
+ self.label.text = self.full_text
678
+
679
+ class PygletScreenShake(PygameScreenShake):
680
+ def apply_to_window(self, window):
681
+ offset = self.shake()
682
+ window.view = window.view.from_translation((offset.x, offset.y, 0))
683
+
684
+ class PygletDraggableSlider:
685
+ def __init__(self, color, starting_x, y, min_x, max_x, batch=None, window=None):
686
+ self.min_x = min_x
687
+ self.max_x = max_x
688
+ self.y = y
689
+ self.dragging = False
690
+
691
+ self.track = pyglet.shapes.Line(min_x, y, max_x, y, width=2,
692
+ color=(100, 100, 100, 255), batch=batch)
693
+
694
+ self.handle = pyglet.shapes.Circle(starting_x, y, 10, color=color, batch=batch)
695
+
696
+ if window:
697
+ window.push_handlers(on_mouse_press=self.on_mouse_press,
698
+ on_mouse_release=self.on_mouse_release,
699
+ on_mouse_drag=self.on_mouse_drag)
700
+
701
+ def on_mouse_press(self, x, y, button, modifiers):
702
+ dist = ((x - self.handle.x)**2 + (y - self.handle.y)**2)**0.5
703
+ if dist < 15:
704
+ self.dragging = True
705
+
706
+ def on_mouse_release(self, x, y, button, modifiers):
707
+ self.dragging = False
708
+
709
+ def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
710
+ if self.dragging:
711
+ # Constrain movement to the min/max X boundaries
712
+ self.handle.x = max(self.min_x, min(x, self.max_x))
713
+
714
+ def get_value(self):
715
+ total_range = self.max_x - self.min_x
716
+ current_pos = self.handle.x - self.min_x
717
+ return current_pos / total_range
718
+
719
+ class ArcadeVisibleVariable(arcade.Text):
720
+ def __init__(self, font, fontsize, fontcolor, x, y, string, background_t_f, backgroundcolor=None):
721
+ super().__init__(str(string), x, y, color=fontcolor, font_size=fontsize, font_name=font)
722
+ self.bg_enabled = background_t_f
723
+ self.bg_color = backgroundcolor
724
+
725
+ def draw(self):
726
+ if self.bg_enabled and self.bg_color:
727
+ arcade.draw_lrtb_rectangle_filled(
728
+ self.left - 5, self.right + 5,
729
+ self.top + 5, self.bottom - 5,
730
+ self.bg_color
731
+ )
732
+ super().draw()
733
+
734
+ def bigstr(initial_text=""):
735
+ return BigString(initial_text)
736
+
737
+ def bigdec(value, scale=0):
738
+ return BigDecimal(value, scale)
739
+
740
+ bigvector2 = BigVector2
741
+ # --- PYGAME WRAPPERS ---
742
+ class PygameSprite(pygame.sprite.Sprite):
743
+ def __init__(self, x="0.0", y="0.0", width=50, height=50, color=(255, 255, 255)):
744
+ super().__init__()
745
+ self.pos = bigvector2(str(x), str(y))
746
+ self.image = pygame.Surface((width, height))
747
+ self.image.fill(color)
748
+ self.rect = self.image.get_rect()
749
+ self.sync()
750
+
751
+ def sync(self):
752
+ p = self.pos.to_pygame()
753
+ self.rect.topleft = (int(p.x), int(p.y))
754
+
755
+ class PygameGroup(pygame.sprite.Group):
756
+ def draw_all(self, surface):
757
+ self.draw(surface)
758
+
759
+ class PygameLayeredGroup(pygame.sprite.LayeredUpdates):
760
+ pass
761
+
762
+ # --- ARCADE WRAPPERS ---
763
+ class ArcadeSprite(arcade.Sprite):
764
+ def __init__(self, x="0.0", y="0.0", scale=1.0):
765
+ super().__init__(scale=scale)
766
+ self.pos = bigvector2(str(x), str(y))
767
+ self.center_x = self.pos.x.to_float()
768
+ self.center_y = self.pos.y.to_float()
769
+
770
+ class ArcadeGroup(arcade.SpriteList):
771
+ pass
772
+
773
+ # --- PYGLET WRAPPERS ---
774
+ class PygletSprite(pyglet.sprite.Sprite):
775
+ def __init__(self, img, x="0.0", y="0.0", batch=None):
776
+ self.pos = bigvector2(str(x), str(y))
777
+ super().__init__(img, x=self.pos.x.to_float(), y=self.pos.y.to_float(), batch=batch)
778
+
779
+ class PygletGroup(pyglet.graphics.Group):
780
+ pass
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: easycode-infinite
3
+ Version: 1.0.0
4
+ Summary: A simple GUI addon for Pygame, Pyglet, and Arcade
5
+ License-File: LICENSE
6
+ Requires-Dist: pygame-ce
7
+ Requires-Dist: pyglet>=2.0
8
+ Requires-Dist: arcade>=3.0
9
+ Requires-Dist: roman
10
+ Dynamic: license-file
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ easycode-infinite/__init__.py
5
+ easycode-infinite/easycode_addons.py
6
+ easycode_infinite.egg-info/PKG-INFO
7
+ easycode_infinite.egg-info/SOURCES.txt
8
+ easycode_infinite.egg-info/dependency_links.txt
9
+ easycode_infinite.egg-info/requires.txt
10
+ easycode_infinite.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ pygame-ce
2
+ pyglet>=2.0
3
+ arcade>=3.0
4
+ roman
@@ -0,0 +1 @@
1
+ easycode-infinite
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "easycode-infinite"
3
+ version = "1.0.0"
4
+ description = "A simple GUI addon for Pygame, Pyglet, and Arcade"
5
+ dependencies = [
6
+ "pygame-ce",
7
+ "pyglet>=2.0",
8
+ "arcade>=3.0",
9
+ "roman"
10
+ ]
11
+
12
+ [build-system]
13
+ requires = ["setuptools>=64", "wheel"]
14
+ build-backend = "setuptools.build_meta"
15
+
16
+ [tool.setuptools]
17
+ packages = ["easycode-infinite"]
18
+ [tool.setuptools.package-data]
19
+ easycode = ["sounds/*.wav"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+