crystalwindow 1.4.8__py3-none-any.whl → 2.5.7__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.

Potentially problematic release.


This version of crystalwindow might be problematic. Click here for more details.

@@ -0,0 +1,254 @@
1
+ Metadata-Version: 2.4
2
+ Name: crystalwindow
3
+ Version: 2.5.7
4
+ Summary: Easier Pygame!, Made by Crystal!!
5
+ Author: CrystalBallyHereXD
6
+ Author-email: mavilla.519@gmail.com
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.1
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: pygame>=2.3.0
12
+ Dynamic: author
13
+ Dynamic: author-email
14
+ Dynamic: classifier
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: requires-dist
18
+ Dynamic: requires-python
19
+ Dynamic: summary
20
+
21
+ # CRYSTALWINDOW!!!
22
+
23
+ A tiny but mighty Pygame framework that gives u a full window system, GUI magic, physics, and file power — all packed into one clean lil module. made by crystal
24
+
25
+ No setup pain. No folder chaos.
26
+ Just import it. Boom. Instant game window. 🎮
27
+
28
+ # Quick Start
29
+ pip install crystalwindow
30
+
31
+
32
+ then make a new .py file:
33
+
34
+ from crystalwindow import Window # imports everything from crystalwindow (in this case its Window)
35
+
36
+ win = Window(800, 600, "Crystal Demo") # setup: Window(width, height, name, icon=MyIcon.ico)
37
+ win.run() # runs the window loop
38
+ win.quit() # closes it (for RAM n CPU)
39
+
40
+
41
+ Run it. and boom, instant working window.
42
+ Yes, THAT easy.
43
+
44
+ # Easy crystalwindow window + Gui + Text file:
45
+ from crystalwindow import *
46
+
47
+ win = Window(800, 600, "CrystalWindowLib Mega Sandbox")
48
+
49
+ gui = GUIManager()
50
+ toggle1 = Toggle((50, 50, 100, 40), value=False)
51
+ slider1 = Slider((50, 120, 200, 30), min_val=0, max_val=100, value=50)
52
+
53
+ btn1 = Button(rect=(50, 200, 150, 50), text="Click Me!", color=random_color(),
54
+ hover_color=random_color(), callback=lambda: print("Button clicked!"))
55
+ lbl1 = Label((250, 50), "Hello GUI!", color=random_color(), size=24)
56
+
57
+ gui.add(toggle1)
58
+ gui.add(slider1)
59
+ gui.add(btn1)
60
+ gui.add(lbl1)
61
+
62
+ # --- Debug Overlay ---
63
+ debug = DebugOverlay()
64
+
65
+ # --- Camera Shake ---
66
+ shake = CameraShake(intensity=20)
67
+
68
+ # --- Main loop ---
69
+ def update(win):
70
+ gui.update(win)
71
+ gui.draw(win)
72
+
73
+ # --- draw text examples ---
74
+ win.draw_text_later("Normal Text", pos=(400, 50), size=18, color=random_color())
75
+ win.draw_text_later("Bold Text", pos=(400, 80), size=20, color=random_color(), bold=True)
76
+ win.draw_text_later("Italic Text", pos=(400, 110), size=20, color=random_color(), italic=True)
77
+ win.draw_text_later("Bold + Italic", pos=(400, 140), size=22, color=random_color(), bold=True, italic=True)
78
+
79
+ # --- draw toggle/slider values ---
80
+ win.draw_text_later(f"Toggle: {toggle1.value}", pos=(50, 90), size=18)
81
+ win.draw_text_later(f"Slider: {int(slider1.value)}", pos=(50, 160), size=18)
82
+
83
+ # --- draw gradient ---
84
+ gradient_rect(win, (50, 300, 200, 100), (255,0,0), (0,0,255))
85
+
86
+ # --- screen shake example (move a rectangle with shake) ---
87
+ shake.update()
88
+ x_off, y_off = shake.offset
89
+ win.draw_rect((0,255,0), (500+x_off, 300+y_off, 100, 50))
90
+
91
+ # --- draw random name + color ---
92
+ win.draw_text_later(f"Random Name: {random_name()}", pos=(50, 420), size=20, color=random_color())
93
+
94
+ # --- debug overlay ---
95
+ debug.draw(win, fps=int(win.clock.get_fps()))
96
+
97
+ win.run(update)
98
+ win.quit()
99
+
100
+ And now thats how you use it!
101
+
102
+ # Current Variables.
103
+
104
+ # Whats Inside
105
+
106
+ Built-in window manager
107
+
108
+ Built-in GUI (buttons, sliders, toggles, labels)
109
+
110
+ Built-in gravity + physics engine
111
+
112
+ Tilemap system (place & save blocks!)
113
+
114
+ Image loader (with default base64 logo)
115
+
116
+ Safe startup (works even inside PyInstaller)
117
+
118
+ No external dependencies (only pygame)
119
+
120
+ Works offline
121
+
122
+ Minimal syntax
123
+
124
+ Full debug overlay
125
+
126
+ # Window System
127
+ from crystalwindow import *
128
+
129
+ win = Window(800, 600, "My Game", icon="MyIcon.png")
130
+
131
+ def loop(win):
132
+ win.fill((10, 10, 30))
133
+ # draw or update stuff here
134
+
135
+ win.run(loop)
136
+ win.quit()
137
+
138
+ # Features
139
+ * handles events
140
+ * tracks keys + mouse
141
+ * supports fullscreen
142
+ * safe to close anytime
143
+
144
+ # Player Example
145
+ player = Player(100, 100)
146
+
147
+ def loop(win):
148
+ player.update(win.keys)
149
+ player.draw(win.screen)
150
+ move(dx, dy) -> moves player
151
+ take_damage(x) -> takes damage
152
+ heal(x) -> heals
153
+ draw(surface) -> renders sprite
154
+
155
+ # TileMap
156
+ tilemap = TileMap(32)
157
+ tilemap.add_tile(5, 5, "grass")
158
+ tilemap.save("level.json")
159
+ add_tile(x, y, type)
160
+ remove_tile(x, y)
161
+ draw(surface)
162
+ save(file) / load(file)
163
+
164
+ # GUI System
165
+ btn = Button(20, 20, 120, 40, "Click Me!", lambda: print("yo"))
166
+ gui = GUIManager()
167
+ gui.add(btn)
168
+
169
+
170
+ Use built-in stuff like:
171
+
172
+ Button(x, y, w, h, text, onclick)
173
+ Label(x, y, text)
174
+ Toggle(x, y, w, h, text, default=False)
175
+ Slider(x, y, w, min, max, default)
176
+
177
+ # Gravity
178
+ g = Gravity(0.5)
179
+ g.update(player)
180
+
181
+
182
+ makes objects fall realistically. ez.
183
+
184
+ # FileHelper
185
+ save_json("data.json", {"coins": 99})
186
+ data = load_json("data.json")
187
+
188
+
189
+ Also supports:
190
+
191
+ save_pickle / load_pickle
192
+
193
+ FileDialog("save") (tkinter dialog popup)
194
+
195
+ # DrawHelper
196
+ DrawHelper.text(win.screen, "Hello!", (10,10), (255,255,255), 24)
197
+ DrawHelper.rect(win.screen, (100,0,200), (50,50,100,60))
198
+
199
+
200
+ perfect for ui & quick visuals
201
+
202
+ # Debug Tools
203
+ debug = DebugOverlay()
204
+ debug.toggle() # show or hide FPS
205
+ debug.draw(win.screen, {"hp": 100, "fps": win.clock.get_fps()})
206
+
207
+ # Example Game
208
+ from crystalwindow import *
209
+
210
+ win = Window(800, 600, "My Cool Game")
211
+ player = Player(100, 100)
212
+ gravity = Gravity()
213
+
214
+ def update(win):
215
+ win.fill((25, 25, 40))
216
+ player.update(win.keys)
217
+ gravity.update(player)
218
+ player.draw(win.screen)
219
+
220
+ win.run(update)
221
+ win.quit()
222
+
223
+ # Default Logo
224
+
225
+ There is a lil encoded PNG inside the file called:
226
+
227
+ DEFAULT_LOGO_BASE64
228
+
229
+
230
+ Its used when no icon is given.
231
+ Set ur own like:
232
+
233
+ Window(800, 600, "My Window", icon="MyIcon.png")
234
+
235
+
236
+ or do whatever you want.. i guess.
237
+
238
+ # Example Integration
239
+ from crystalwindow import Window
240
+
241
+ win = Window(800, 600, "My Window", icon="MyIcon.png")
242
+
243
+ while win.running:
244
+ win.check_events()
245
+ win.fill((10, 10, 20))
246
+ win.run()
247
+ win.quit()
248
+
249
+ # Credits
250
+
251
+ Made by: CrystalBallyHereXD
252
+ Framework: CrystalWindow
253
+ Powered by: Pygame
254
+ License: Free to use, modify, and vibe with it!
@@ -0,0 +1,4 @@
1
+ crystalwindow-2.5.7.dist-info/METADATA,sha256=VzvsETk32S3poHnNl_8TYokr9NwXySY2T6FC4ghx5Nc,6539
2
+ crystalwindow-2.5.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3
+ crystalwindow-2.5.7.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
4
+ crystalwindow-2.5.7.dist-info/RECORD,,
@@ -1,92 +0,0 @@
1
- import os
2
- import json
3
- import pickle
4
- import tkinter as tk
5
- from tkinter import filedialog
6
-
7
-
8
- class FileHelper:
9
- """CrystalWindow integrated file helper with default folders & Tk dialogs."""
10
-
11
- def __init__(self, default_save_folder="saves"):
12
- # auto-make folder if not found
13
- self.default_save_folder = default_save_folder
14
- os.makedirs(self.default_save_folder, exist_ok=True)
15
-
16
- # ---- TK FILE DIALOGS ----
17
- def ask_save_file(self, default_name="save.json",
18
- filetypes=[("JSON files", "*.json"), ("All files", "*.*")]):
19
- """Open a Tkinter Save dialog inside default folder."""
20
- root = tk.Tk()
21
- root.withdraw()
22
- path = filedialog.asksaveasfilename(
23
- initialdir=self.default_save_folder,
24
- initialfile=default_name,
25
- filetypes=filetypes,
26
- defaultextension=filetypes[0][1]
27
- )
28
- root.destroy()
29
- return path
30
-
31
- def ask_open_file(self,
32
- filetypes=[("JSON files", "*.json"), ("All files", "*.*")]):
33
- """Open a Tkinter Open dialog inside default folder."""
34
- root = tk.Tk()
35
- root.withdraw()
36
- path = filedialog.askopenfilename(
37
- initialdir=self.default_save_folder,
38
- filetypes=filetypes
39
- )
40
- root.destroy()
41
- return path
42
-
43
- # ---- TEXT ----
44
- def save_text(self, filename, content):
45
- """Save plain text data to file."""
46
- path = os.path.join(self.default_save_folder, filename)
47
- with open(path, "w", encoding="utf-8") as f:
48
- f.write(content)
49
- return path
50
-
51
- def load_text(self, filename):
52
- """Load plain text data from file."""
53
- path = os.path.join(self.default_save_folder, filename)
54
- if os.path.exists(path):
55
- with open(path, "r", encoding="utf-8") as f:
56
- return f.read()
57
- print(f"[WARN] Text file not found: {filename}")
58
- return None
59
-
60
- # ---- JSON ----
61
- def save_json(self, filename, data):
62
- """Save JSON serializable data."""
63
- path = os.path.join(self.default_save_folder, filename)
64
- with open(path, "w", encoding="utf-8") as f:
65
- json.dump(data, f, indent=4)
66
- return path
67
-
68
- def load_json(self, filename):
69
- """Load JSON file."""
70
- path = os.path.join(self.default_save_folder, filename)
71
- if os.path.exists(path):
72
- with open(path, "r", encoding="utf-8") as f:
73
- return json.load(f)
74
- print(f"[WARN] JSON file not found: {filename}")
75
- return None
76
-
77
- # ---- PICKLE ----
78
- def save_pickle(self, filename, obj):
79
- """Save object using pickle (binary)."""
80
- path = os.path.join(self.default_save_folder, filename)
81
- with open(path, "wb") as f:
82
- pickle.dump(obj, f)
83
- return path
84
-
85
- def load_pickle(self, filename):
86
- """Load pickle file (binary)."""
87
- path = os.path.join(self.default_save_folder, filename)
88
- if os.path.exists(path):
89
- with open(path, "rb") as f:
90
- return pickle.load(f)
91
- print(f"[WARN] Pickle file not found: {filename}")
92
- return None
crystalwindow/__init__.py DELETED
@@ -1,47 +0,0 @@
1
- # crystalwindow/__init__.py
2
- # 🪞 CrystalWindow - Master import hub
3
-
4
- # --- Core systems ---
5
- from .window import Window
6
- from .sprites import Sprite
7
- from .tilemap import TileMap
8
- from .player import Player
9
- from .gravity import Gravity
10
- from .FileHelper import FileHelper
11
-
12
- # --- Assets & Animations ---
13
- from .assets import load_image, load_folder_images, load_music, play_music
14
- from .animation import Animation
15
-
16
- # --- Collision ---
17
- from .collision import check_collision, resolve_collision
18
-
19
- # --- GUI & Extensions ---
20
- from .gui import Button, Label, GUIManager, random_color, hex_to_rgb
21
- from .gui_ext import Toggle, Slider
22
-
23
- # --- Drawing Helpers ---
24
- from .draw_helpers import gradient_rect, CameraShake
25
- from .draw_rects import DrawHelper
26
- from .draw_text_helper import DrawTextManager
27
-
28
- # --- Misc Helpers ---
29
- from .fun_helpers import random_name, DebugOverlay
30
-
31
-
32
- __all__ = [
33
- # Core
34
- "Window", "Sprite", "TileMap", "Player", "Gravity", "FileHelper",
35
- # Assets & Animation
36
- "load_image", "load_folder_images", "load_music", "play_music", "Animation",
37
- # Collision
38
- "check_collision", "resolve_collision",
39
- # GUI
40
- "Button", "Label", "GUIManager", "random_color", "hex_to_rgb",
41
- # GUI Ext
42
- "Toggle", "Slider",
43
- # Drawing
44
- "gradient_rect", "CameraShake", "DrawHelper", "DrawTextManager",
45
- # Misc
46
- "random_name", "DebugOverlay"
47
- ]
@@ -1,15 +0,0 @@
1
- class Animation:
2
- def __init__(self, frames, speed=0.1):
3
- self.frames = frames
4
- self.speed = speed
5
- self.current = 0
6
- self.timer = 0
7
-
8
- def update(self, dt):
9
- self.timer += dt
10
- if self.timer >= self.speed:
11
- self.timer = 0
12
- self.current = (self.current + 1) % len(self.frames)
13
-
14
- def get_frame(self):
15
- return self.frames[self.current]
crystalwindow/assets.py DELETED
@@ -1,33 +0,0 @@
1
- import pygame
2
- import os
3
-
4
- ASSETS = {}
5
-
6
- def load_image(path, size=None):
7
- if not os.path.exists(path):
8
- raise FileNotFoundError(f"Image not found: {path}")
9
- img = pygame.image.load(path).convert_alpha()
10
- if size:
11
- img = pygame.transform.scale(img, size)
12
- ASSETS[path] = img
13
- return img
14
-
15
- def load_folder_images(folder, size=None, nested=True):
16
- result = {}
17
- for item in os.listdir(folder):
18
- full_path = os.path.join(folder, item)
19
- if os.path.isdir(full_path) and nested:
20
- result[item] = load_folder_images(full_path, size=size, nested=True)
21
- elif item.lower().endswith((".png", ".jpg", ".jpeg")):
22
- result[item] = load_image(full_path, size=size)
23
- return result
24
-
25
- def load_music(path):
26
- if not os.path.exists(path):
27
- raise FileNotFoundError(f"Music not found: {path}")
28
- pygame.mixer.music.load(path)
29
- ASSETS[path] = path
30
- return path
31
-
32
- def play_music(loop=-1):
33
- pygame.mixer.music.play(loop)
@@ -1,15 +0,0 @@
1
- def check_collision(sprite, tiles):
2
- for t in tiles:
3
- if sprite.rect.colliderect(t.rect):
4
- return True
5
- return False
6
-
7
- def resolve_collision(sprite, tiles, dy):
8
- sprite.rect.y += dy
9
- collided = [t for t in tiles if sprite.rect.colliderect(t.rect)]
10
- for t in collided:
11
- if dy > 0:
12
- sprite.rect.bottom = t.rect.top
13
- elif dy < 0:
14
- sprite.rect.top = t.rect.bottom
15
- sprite.y = sprite.rect.y
@@ -1,23 +0,0 @@
1
- from .window import Window
2
-
3
- def gradient_rect(win, rect, color_start, color_end, vertical=True):
4
- x,y,w,h = rect
5
- for i in range(h if vertical else w):
6
- t = i/(h if vertical else w)
7
- r = int(color_start[0]*(1-t) + color_end[0]*t)
8
- g = int(color_start[1]*(1-t) + color_end[1]*t)
9
- b = int(color_start[2]*(1-t) + color_end[2]*t)
10
- if vertical:
11
- win.draw_rect((r,g,b), (x,y+i,w,1))
12
- else:
13
- win.draw_rect((r,g,b), (x+i,y,1,h))
14
-
15
- class CameraShake:
16
- def __init__(self, intensity=5):
17
- self.intensity = intensity
18
- self.offset = (0,0)
19
-
20
- def update(self):
21
- import random
22
- self.offset = (random.randint(-self.intensity,self.intensity),
23
- random.randint(-self.intensity,self.intensity))
@@ -1,51 +0,0 @@
1
- import pygame as py
2
- from crystalwindow import *
3
-
4
- class DrawHelper:
5
- def __init__(self):
6
- pass
7
-
8
- def rect(self, win, x, y, w, h, color):
9
- py.draw.rect(win.screen, color, (x, y, w, h))
10
- return self
11
-
12
- def square(self, win, x, y, size, color):
13
- py.draw.rect(win.screen, color, (x, y, size, size))
14
- return self
15
-
16
- def circle(self, win, x, y, radius, color):
17
- py.draw.circle(win.screen, color, (x, y), radius)
18
- return self
19
-
20
- def triangle(self, win, points, color):
21
- py.draw.polygon(win.screen, color, points)
22
- return self
23
-
24
- def texture(self, win, x, y, w, h, image):
25
- if image:
26
- img = py.transform.scale(image, (w, h))
27
- win.screen.blit(img, (x, y))
28
- return self
29
-
30
- def text(self, win, text, font="Arial", size=16, x=0, y=0, color=(255,255,255), bold=False, cursive=False):
31
- fnt = py.font.SysFont(font, size, bold=bold, italic=cursive)
32
- surf = fnt.render(text, True, color)
33
- win.screen.blit(surf, (x, y))
34
- return self
35
-
36
- def gradient_rect(self, win, x, y, w, h, start_color, end_color, vertical=True):
37
- if vertical:
38
- for i in range(h):
39
- ratio = i / h
40
- r = int(start_color[0]*(1-ratio) + end_color[0]*ratio)
41
- g = int(start_color[1]*(1-ratio) + end_color[1]*ratio)
42
- b = int(start_color[2]*(1-ratio) + end_color[2]*ratio)
43
- py.draw.line(win.screen, (r, g, b), (x, y+i), (x+w, y+i))
44
- else:
45
- for i in range(w):
46
- ratio = i / w
47
- r = int(start_color[0]*(1-ratio) + end_color[0]*ratio)
48
- g = int(start_color[1]*(1-ratio) + end_color[1]*ratio)
49
- b = int(start_color[2]*(1-ratio) + end_color[2]*ratio)
50
- py.draw.line(win.screen, (r, g, b), (x+i, y), (x+i, y+h))
51
- return self
@@ -1,45 +0,0 @@
1
- from .window import Window
2
-
3
- class DrawTextManager:
4
- def __init__(self):
5
- self.texts = []
6
-
7
- def add_text(self, text, pos=(0,0), size=16, color=(255,255,255), bold=False, italic=False, duration=None):
8
- """
9
- text: str
10
- pos: (x,y)
11
- size: int
12
- color: RGB tuple
13
- bold: bool
14
- italic: bool
15
- duration: float seconds or None for permanent
16
- """
17
- self.texts.append({
18
- "text": text,
19
- "pos": pos,
20
- "size": size,
21
- "color": color,
22
- "bold": bold,
23
- "italic": italic,
24
- "duration": duration,
25
- "timer": 0
26
- })
27
-
28
- def update(self, dt):
29
- # update timers and remove expired text
30
- for t in self.texts[:]:
31
- if t["duration"] is not None:
32
- t["timer"] += dt
33
- if t["timer"] >= t["duration"]:
34
- self.texts.remove(t)
35
-
36
- def draw(self, win: Window):
37
- for t in self.texts:
38
- win.draw_text(
39
- t["text"],
40
- pos=t["pos"],
41
- size=t["size"],
42
- color=t["color"],
43
- bold=t["bold"],
44
- italic=t["italic"]
45
- )
@@ -1,28 +0,0 @@
1
- import random
2
-
3
- # random color helpers
4
- def random_color():
5
- return (random.randint(0,255), random.randint(0,255), random.randint(0,255))
6
-
7
- def random_palette(n=5):
8
- return [random_color() for _ in range(n)]
9
-
10
- # random name generator
11
- def random_name():
12
- syllables = ["ka","zi","lo","ra","mi","to","na","ve"]
13
- return "".join(random.choice(syllables) for _ in range(3))
14
-
15
- # tween helper
16
- def lerp(a, b, t):
17
- return a + (b - a) * t
18
-
19
- # debug overlay
20
- class DebugOverlay:
21
- def __init__(self):
22
- self.active = True
23
-
24
- def draw(self, win, fps=60):
25
- if self.active:
26
- win.draw_text(f"FPS: {fps}", pos=(10,10))
27
- mx,my = win.mouse_pos
28
- win.draw_text(f"Mouse: {mx},{my}", pos=(10,30))
crystalwindow/gravity.py DELETED
@@ -1,75 +0,0 @@
1
- class Gravity:
2
- def __init__(self, obj, force=1, terminal_velocity=15, bouncy=False, bounce_strength=0.7):
3
- self.obj = obj
4
- if not hasattr(self.obj, "vel_y"):
5
- self.obj.vel_y = 0
6
- self.force = force
7
- self.terminal_velocity = terminal_velocity
8
- self.bouncy = bouncy
9
- self.bounce_strength = bounce_strength
10
- self.on_ground = False
11
- self.stretch_factor = 0 # for squishy effect
12
-
13
- # --- NEW: choose mode ---
14
- # If object has .sprite use that, else fallback to rect mode
15
- if hasattr(obj, "sprite"):
16
- self.mode = "sprite"
17
- else:
18
- self.mode = "rect"
19
-
20
- def get_obj_rect(self):
21
- # helper to get x,y,w,h depending on mode
22
- if self.mode == "sprite":
23
- s = self.obj.sprite
24
- x = getattr(s, "x", 0)
25
- y = getattr(s, "y", 0)
26
- w = getattr(s, "width", getattr(s, "w", 32))
27
- h = getattr(s, "height", getattr(s, "h", 32))
28
- else:
29
- x = getattr(self.obj, "x", 0)
30
- y = getattr(self.obj, "y", 0)
31
- w = getattr(self.obj, "width", getattr(self.obj, "w", 32))
32
- h = getattr(self.obj, "height", getattr(self.obj, "h", 32))
33
- return x, y, w, h
34
-
35
- def update(self, dt, platforms=[]):
36
- # apply gravity
37
- self.obj.vel_y += self.force * dt * 60
38
- if self.obj.vel_y > self.terminal_velocity:
39
- self.obj.vel_y = self.terminal_velocity
40
-
41
- # move object
42
- x, y, w, h = self.get_obj_rect()
43
- y += self.obj.vel_y
44
- self.on_ground = False
45
-
46
- # check collisions
47
- for plat in platforms:
48
- plat_w = getattr(plat, "width", getattr(plat, "w", 0))
49
- plat_h = getattr(plat, "height", getattr(plat, "h", 0))
50
- if (x + w > plat.x and x < plat.x + plat_w and
51
- y + h > plat.y and y < plat.y + plat_h):
52
-
53
- y = plat.y - h
54
- self.on_ground = True
55
-
56
- if self.bouncy:
57
- self.obj.vel_y = -self.obj.vel_y * self.bounce_strength
58
- self.stretch_factor = min(0.5, self.stretch_factor + 0.2)
59
- else:
60
- self.obj.vel_y = 0
61
- self.stretch_factor = 0
62
-
63
- # slowly reset stretch
64
- if self.stretch_factor > 0:
65
- self.stretch_factor -= 0.05
66
- if self.stretch_factor < 0:
67
- self.stretch_factor = 0
68
-
69
- # write back position
70
- if self.mode == "sprite":
71
- self.obj.sprite.x = x
72
- self.obj.sprite.y = y
73
- else:
74
- self.obj.x = x
75
- self.obj.y = y
crystalwindow/gui.py DELETED
@@ -1,64 +0,0 @@
1
- import pygame
2
- import random
3
-
4
- # ----------------- Color Helpers -----------------
5
- def hex_to_rgb(hex_str):
6
- """Convert hex color string to RGB tuple"""
7
- hex_str = hex_str.lstrip("#")
8
- return tuple(int(hex_str[i:i+2], 16) for i in (0, 2, 4))
9
-
10
- def random_color():
11
- """Return a random RGB color"""
12
- return (random.randint(0,255), random.randint(0,255), random.randint(0,255))
13
-
14
- def lerp_color(c1, c2, t):
15
- """Linearly interpolate between two colors"""
16
- return tuple(int(a + (b-a)*t) for a,b in zip(c1,c2))
17
-
18
- # ----------------- GUI Elements -----------------
19
- class Button:
20
- def __init__(self, rect, text, color=(200,200,200), hover_color=(255,255,255), callback=None):
21
- self.rect = pygame.Rect(rect)
22
- self.text = text
23
- self.color = color
24
- self.hover_color = hover_color
25
- self.callback = callback
26
-
27
- def draw(self, win):
28
- mouse_pos = win.mouse_pos
29
- cur_color = self.hover_color if self.rect.collidepoint(mouse_pos) else self.color
30
- win.draw_rect(cur_color, self.rect)
31
- win.draw_text(self.text, pos=(self.rect.x+5, self.rect.y+5))
32
-
33
- def check_click(self, win):
34
- if self.rect.collidepoint(win.mouse_pos) and win.is_mouse_pressed(1):
35
- if self.callback:
36
- self.callback()
37
-
38
- class Label:
39
- def __init__(self, pos, text, color=(255,255,255), font="Arial", size=16):
40
- self.pos = pos
41
- self.text = text
42
- self.color = color
43
- self.font = font
44
- self.size = size
45
-
46
- def draw(self, win):
47
- win.draw_text(self.text, font=self.font, size=self.size, color=self.color, pos=self.pos)
48
-
49
- # ----------------- Optional GUI Manager -----------------
50
- class GUIManager:
51
- def __init__(self):
52
- self.elements = []
53
-
54
- def add(self, element):
55
- self.elements.append(element)
56
-
57
- def draw(self, win):
58
- for e in self.elements:
59
- e.draw(win)
60
-
61
- def update(self, win):
62
- for e in self.elements:
63
- if isinstance(e, Button):
64
- e.check_click(win)
crystalwindow/gui_ext.py DELETED
@@ -1,62 +0,0 @@
1
- from .window import Window
2
- from .assets import load_image
3
- from .sprites import Sprite
4
- from .gui import Button
5
-
6
- class Toggle:
7
- def __init__(self, rect, value=False, color=(200,200,200), hover_color=(255,255,255)):
8
- self.rect = rect
9
- self.value = value
10
- self.color = color
11
- self.hover_color = hover_color
12
- self.hovered = False
13
-
14
- def update(self, win):
15
- mx, my = win.mouse_pos
16
- x,y,w,h = self.rect
17
- self.hovered = x <= mx <= x+w and y <= my <= y+h
18
- if self.hovered and win.is_mouse_pressed(1):
19
- self.value = not self.value
20
-
21
- def draw(self, win):
22
- draw_color = self.hover_color if self.hovered else self.color
23
- win.draw_rect(draw_color, self.rect)
24
-
25
- from .window import Window
26
-
27
- class Slider:
28
- def __init__(self, rect, min_val=0, max_val=100, value=50, color=(150,150,150), handle_radius=10):
29
- self.rect = rect # x, y, w, h
30
- self.min_val = min_val
31
- self.max_val = max_val
32
- self.value = value
33
- self.color = color
34
- self.handle_radius = handle_radius
35
- self.dragging = False
36
-
37
- def update(self, win: Window):
38
- mx, my = win.mouse_pos
39
- x, y, w, h = self.rect
40
-
41
- # Start dragging if clicked on the slider track or handle
42
- handle_x = x + ((self.value - self.min_val) / (self.max_val - self.min_val)) * w
43
- handle_y = y + h // 2
44
- if win.is_mouse_pressed(1):
45
- if (x <= mx <= x + w and y <= my <= y + h) or ((mx-handle_x)**2 + (my-handle_y)**2 <= self.handle_radius**2):
46
- self.dragging = True
47
- else:
48
- self.dragging = False
49
-
50
- # Drag the handle
51
- if self.dragging:
52
- rel_x = max(0, min(mx - x, w))
53
- self.value = self.min_val + (rel_x / w) * (self.max_val - self.min_val)
54
-
55
- def draw(self, win: Window):
56
- x, y, w, h = self.rect
57
- # draw slider track
58
- win.draw_rect(self.color, (x, y + h//2 - 2, w, 4))
59
- # draw handle as circle
60
- handle_x = x + ((self.value - self.min_val) / (self.max_val - self.min_val)) * w
61
- handle_y = y + h // 2
62
- win.draw_circle((255,0,0), (int(handle_x), int(handle_y)), self.handle_radius)
crystalwindow/player.py DELETED
@@ -1,106 +0,0 @@
1
- import math, os, re
2
- from .assets import load_folder_images
3
- from .animation import Animation
4
-
5
- class Player:
6
- def __init__(self, pos=(0, 0), speed=5, size=(32, 32)):
7
- self.x, self.y = pos
8
- self.speed = speed
9
- self.width, self.height = size
10
- self.animations = {}
11
- self.current_anim = None
12
- self.image = None
13
- self.rect = self._make_rect()
14
-
15
- # make a lil rect obj for collisions
16
- def _make_rect(self):
17
- return type("Rect", (), {"x": self.x, "y": self.y, "w": self.width, "h": self.height,
18
- "topleft": (self.x, self.y)})()
19
-
20
- # === animation setup ===
21
- def idle(self, folder):
22
- imgs = self._load_sorted(folder)
23
- anim = Animation(imgs)
24
- self.animations["idle"] = anim
25
- self.current_anim = anim
26
- return anim
27
-
28
- class walk:
29
- @staticmethod
30
- def cycle(folder):
31
- imgs = Player._load_sorted_static(folder)
32
- anim = Animation(imgs)
33
- anim.loop = True
34
- return anim
35
-
36
- @staticmethod
37
- def once(folder):
38
- imgs = Player._load_sorted_static(folder)
39
- anim = Animation(imgs)
40
- anim.loop = False
41
- return anim
42
-
43
- class jump:
44
- @staticmethod
45
- def cycle(folder):
46
- imgs = Player._load_sorted_static(folder)
47
- anim = Animation(imgs)
48
- anim.loop = True
49
- return anim
50
-
51
- @staticmethod
52
- def once(folder):
53
- imgs = Player._load_sorted_static(folder)
54
- anim = Animation(imgs)
55
- anim.loop = False
56
- return anim
57
-
58
- # === main update ===
59
- def update(self, dt, win):
60
- moving = False
61
- if win.is_key_pressed("left"):
62
- self.x -= self.speed * dt * 60
63
- moving = True
64
- if win.is_key_pressed("right"):
65
- self.x += self.speed * dt * 60
66
- moving = True
67
- if win.is_key_pressed("up"):
68
- self.y -= self.speed * dt * 60
69
- moving = True
70
- if win.is_key_pressed("down"):
71
- self.y += self.speed * dt * 60
72
- moving = True
73
-
74
- # update animation
75
- if moving and "run" in self.animations:
76
- self.current_anim = self.animations["run"]
77
- elif not moving and "idle" in self.animations:
78
- self.current_anim = self.animations["idle"]
79
-
80
- if self.current_anim:
81
- self.current_anim.update(dt)
82
- self.image = self.current_anim.get_frame()
83
-
84
- self.rect.x, self.rect.y = self.x, self.y
85
- self.rect.topleft = (self.x, self.y)
86
-
87
- def draw(self, win):
88
- if self.image:
89
- win.screen.blit(self.image, (self.x, self.y))
90
-
91
- # ---------- helpers ----------
92
- def _load_sorted(self, folder):
93
- imgs_dict = load_folder_images(folder)
94
- return self._sort_images(imgs_dict)
95
-
96
- @staticmethod
97
- def _load_sorted_static(folder):
98
- imgs_dict = load_folder_images(folder)
99
- return Player._sort_images(imgs_dict)
100
-
101
- @staticmethod
102
- def _sort_images(imgs_dict):
103
- def extract_num(filename):
104
- match = re.search(r'(\d+)', filename)
105
- return int(match.group(1)) if match else 0
106
- return [img for name, img in sorted(imgs_dict.items(), key=lambda x: extract_num(x[0]))]
crystalwindow/sprites.py DELETED
@@ -1,30 +0,0 @@
1
- import pygame
2
-
3
- class Sprite:
4
- def __init__(self, pos, size=None, image=None, color=(255, 0, 0)):
5
- """
6
- pos: (x, y) topleft
7
- size: (w, h) if you want a plain rect
8
- image: pygame.Surface if u have an image
9
- color: fill color if size is used
10
- """
11
- self.pos = pos
12
- self.x, self.y = pos # <- add this here, not in __init__.py
13
-
14
- if image is not None:
15
- self.image = image
16
- elif size is not None:
17
- self.image = pygame.Surface(size)
18
- self.image.fill(color)
19
- else:
20
- raise ValueError("Sprite needs either size or image")
21
-
22
- self.rect = self.image.get_rect(topleft=pos)
23
-
24
- def draw(self, win):
25
- win.blit(self.image, self.rect)
26
-
27
- def move(self, dx, dy):
28
- self.x += dx
29
- self.y += dy
30
- self.rect.topleft = (self.x, self.y)
crystalwindow/tilemap.py DELETED
@@ -1,13 +0,0 @@
1
- from .sprites import Sprite
2
-
3
- class TileMap:
4
- def __init__(self, tile_size):
5
- self.tile_size = tile_size
6
- self.tiles = []
7
-
8
- def add_tile(self, image, x, y):
9
- self.tiles.append(Sprite(image, x, y))
10
-
11
- def draw(self, win):
12
- for t in self.tiles:
13
- win.draw_sprite(t)
crystalwindow/window.py DELETED
@@ -1,211 +0,0 @@
1
- import pygame
2
- from pygame import image
3
- import os
4
- import sys
5
- import contextlib
6
- import base64, io
7
-
8
- DEFAULT_LOGO_BASE64 = "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"
9
-
10
- def decode_logo():
11
- """Decode the base64 logo and return a pygame surface."""
12
- logo_data = base64.b64decode(DEFAULT_LOGO_BASE64)
13
- with io.BytesIO(logo_data) as logo_bytes:
14
- return image.load(logo_bytes)
15
- class Window:
16
- # friendly string -> pygame constant mapping
17
- KEY_MAP = {
18
- "left": pygame.K_LEFT,
19
- "right": pygame.K_RIGHT,
20
- "up": pygame.K_UP,
21
- "down": pygame.K_DOWN,
22
- "space": pygame.K_SPACE,
23
- "enter": pygame.K_RETURN,
24
- "backspace": pygame.K_BACKSPACE,
25
- "tab": pygame.K_TAB,
26
- "escape": pygame.K_ESCAPE,
27
- "Rshift": pygame.K_LSHIFT,
28
- "Lctrl": pygame.K_LCTRL,
29
- "Rshift": pygame.K_RSHIFT,
30
- "Rctrl": pygame.K_RCTRL,
31
- "alt": pygame.K_LALT,
32
- "keya": pygame.K_a,
33
- "keyb": pygame.K_b,
34
- "keyc": pygame.K_c,
35
- "keyd": pygame.K_d,
36
- "keye": pygame.K_e,
37
- "keyf": pygame.K_f,
38
- "keyg": pygame.K_g,
39
- "keyh": pygame.K_h,
40
- "keyi": pygame.K_i,
41
- "keyj": pygame.K_j,
42
- "keyk": pygame.K_k,
43
- "keyl": pygame.K_l,
44
- "keym": pygame.K_m,
45
- "keyn": pygame.K_n,
46
- "keyo": pygame.K_o,
47
- "keyp": pygame.K_p,
48
- "keyq": pygame.K_q,
49
- "keyr": pygame.K_r,
50
- "keys": pygame.K_s,
51
- "keyt": pygame.K_t,
52
- "keyu": pygame.K_u,
53
- "keyv": pygame.K_v,
54
- "keyw": pygame.K_w,
55
- "keyx": pygame.K_x,
56
- "keyy": pygame.K_y,
57
- "keyz": pygame.K_z,
58
- "0": pygame.K_0,
59
- "1": pygame.K_1,
60
- "2": pygame.K_2,
61
- "3": pygame.K_3,
62
- "4": pygame.K_4,
63
- "5": pygame.K_5,
64
- "6": pygame.K_6,
65
- "7": pygame.K_7,
66
- "8": pygame.K_8,
67
- "9": pygame.K_9,
68
- "f1": pygame.K_F1,
69
- "f2": pygame.K_F2,
70
- "f3": pygame.K_F3,
71
- "f4": pygame.K_F4,
72
- "f5": pygame.K_F5,
73
- "f6": pygame.K_F6,
74
- "f7": pygame.K_F7,
75
- "f8": pygame.K_F8,
76
- "f9": pygame.K_F9,
77
- "f10": pygame.K_F10,
78
- "f11": pygame.K_F11,
79
- "f12": pygame.K_F12,
80
- "insert": pygame.K_INSERT,
81
- "delete": pygame.K_DELETE,
82
- "home": pygame.K_HOME,
83
- "end": pygame.K_END,
84
- "pageup": pygame.K_PAGEUP,
85
- "pagedown": pygame.K_PAGEDOWN,
86
- "capslock": pygame.K_CAPSLOCK,
87
- "numlock": pygame.K_NUMLOCK,
88
- "scrolllock": pygame.K_SCROLLOCK,
89
- }
90
-
91
-
92
- def __init__(self, width=640, height=480, title="Game Window", icon=None):
93
- # temporarily silence pygame print
94
- with open(os.devnull, "w") as f, contextlib.redirect_stdout(f):
95
- pygame.init() # <- no more hello message
96
-
97
- self.width = width
98
- self.height = height
99
- self.title = title
100
- self.screen = pygame.display.set_mode((width, height))
101
- pygame.display.set_caption(title)
102
-
103
- # --- ICON HANDLING ---
104
- if icon:
105
- # User provided icon logic
106
- if isinstance(icon, str): # Path provided as string
107
- icon_path = icon
108
- if not os.path.isabs(icon_path):
109
- icon_path = os.path.join(os.getcwd(), icon) # Relative path, make it absolute
110
-
111
- if os.path.exists(icon_path): # Valid path
112
- img = pygame.image.load(icon_path).convert_alpha()
113
- pygame.display.set_icon(img)
114
- else: # File not found
115
- print(f"⚠️ Icon file not found: {icon_path}")
116
- img = decode_logo() # Use default base64 logo
117
- pygame.display.set_icon(img)
118
- else:
119
- pygame.display.set_icon(icon) # Use the provided icon directly
120
- else:
121
- # No icon provided, check for default in the local directory
122
- default_icon_path = os.path.join(os.path.dirname(__file__), "icons", "default_icon.png")
123
- if os.path.exists(default_icon_path):
124
- img = pygame.image.load(default_icon_path).convert_alpha()
125
- pygame.display.set_icon(img)
126
- else:
127
- # Use base64 fallback
128
- img = decode_logo()
129
- pygame.display.set_icon(img)
130
-
131
- self.clock = pygame.time.Clock()
132
- self.running = True
133
- self.keys = {}
134
- self.mouse_pos = (0,0)
135
- self.mouse_pressed = (False, False, False)
136
- self.draw_calls = []
137
-
138
- # === input helpers ===
139
- def is_key_pressed(self, key):
140
- # key can be pygame constant OR string from KEY_MAP
141
- if isinstance(key, str):
142
- key = self.KEY_MAP.get(key, None)
143
- if key is None:
144
- return False
145
- return self.keys.get(key, False)
146
-
147
- def is_mouse_pressed(self, button=1):
148
- return self.mouse_pressed[button-1]
149
-
150
- # === font system ===
151
- def get_font(self, name="Arial", size=16, bold=False, italic=False):
152
- if not hasattr(self, "font_cache"):
153
- self.font_cache = {}
154
- key = (name, size, bold, italic)
155
- if key not in self.font_cache:
156
- try:
157
- font = pygame.font.SysFont(name, size, bold, italic)
158
- except:
159
- font = pygame.font.Font(None, size)
160
- self.font_cache[key] = font
161
- return self.font_cache[key]
162
-
163
- def draw_text(self, text, font="Arial", size=16, color=(255,255,255), pos=(0,0), bold=False, italic=False):
164
- f = self.get_font(font, size, bold, italic)
165
- surf = f.render(text, True, color)
166
- self.screen.blit(surf, pos)
167
-
168
- # === drawing helpers ===a
169
- def draw_rect(self, color, rect):
170
- self.draw_calls.append(("rect", color, rect))
171
-
172
- def draw_circle(self, color, pos, radius):
173
- self.draw_calls.append(("circle", color, pos, radius))
174
-
175
- def draw_sprite(self, sprite):
176
- self.draw_calls.append(("sprite", sprite))
177
-
178
- # === main loop ===
179
- def run(self, update_func=None):
180
- while self.running:
181
- for event in pygame.event.get():
182
- if event.type == pygame.QUIT:
183
- self.running = False
184
- elif event.type == pygame.KEYDOWN:
185
- self.keys[event.key] = True
186
- elif event.type == pygame.KEYUP:
187
- self.keys[event.key] = False
188
- elif event.type == pygame.MOUSEBUTTONDOWN:
189
- self.mouse_pressed = pygame.mouse.get_pressed()
190
- elif event.type == pygame.MOUSEBUTTONUP:
191
- self.mouse_pressed = pygame.mouse.get_pressed()
192
- elif event.type == pygame.MOUSEMOTION:
193
- self.mouse_pos = event.pos
194
-
195
- if update_func:
196
- update_func(self)
197
-
198
- self.screen.fill((20,20,50))
199
- for call in self.draw_calls:
200
- if call[0] == "rect":
201
- pygame.draw.rect(self.screen, call[1], call[2])
202
- elif call[0] == "circle":
203
- pygame.draw.circle(self.screen, call[1], call[2], call[3])
204
- elif call[0] == "sprite":
205
- call[1].draw(self.screen)
206
- self.draw_calls.clear()
207
-
208
- pygame.display.flip()
209
- self.clock.tick(60)
210
-
211
- pygame.quit()
@@ -1,19 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: crystalwindow
3
- Version: 1.4.8
4
- Summary: pygame but remade, easy window/player/tile handling
5
- Author: CrystalBallyHereXD
6
- Classifier: Programming Language :: Python :: 3
7
- Classifier: Operating System :: OS Independent
8
- Requires-Python: >=3.8
9
- Description-Content-Type: text/markdown
10
- Requires-Dist: pygame>=2.3.0
11
- Dynamic: author
12
- Dynamic: classifier
13
- Dynamic: description
14
- Dynamic: description-content-type
15
- Dynamic: requires-dist
16
- Dynamic: requires-python
17
- Dynamic: summary
18
-
19
- pygame but easier!. Made by CrystalBallyHereXD!
@@ -1,20 +0,0 @@
1
- crystalwindow/FileHelper.py,sha256=WFnG9I3PhuAwSd-CtenX_kPW_cN1RU8fDY2oTDhimiY,3274
2
- crystalwindow/__init__.py,sha256=al0kboOcNRV-Jo4XVNb2_r9JzTe2Oj2dGB3W-Cpk8kA,1408
3
- crystalwindow/animation.py,sha256=zHjrdBXQeyNaLAuaGPldJueX05OZ5j31YR8NizmR0uQ,427
4
- crystalwindow/assets.py,sha256=LR2ocZ0BUxz8qZqEtx1s-WvxwY60b2OmxlzGj_Hyrao,1022
5
- crystalwindow/collision.py,sha256=hpkHTp_KparghVK-itp_rxuYdd2GbQMxICHlUBv0rSw,472
6
- crystalwindow/draw_helpers.py,sha256=HqjI5fTbdnA55g4LKYEuMUdIjrWaBm2U8RmeUXjcQGw,821
7
- crystalwindow/draw_rects.py,sha256=TT9CA9aSkLXq6VtApkuHEGtRHjazXj_ph2cXCwEYJQQ,1945
8
- crystalwindow/draw_text_helper.py,sha256=CGF2Kk39IxggzY5w-iDA4XzRwxiMDLH0LMBKIQQOO4s,1276
9
- crystalwindow/fun_helpers.py,sha256=fo5yTwGENltdtVIVwgITtUkvzpsImujOcqTxe8XPzR8,760
10
- crystalwindow/gravity.py,sha256=tMctHYubpcUJRogz4kNg3XZGDPE3605zON-4YumiVGo,2752
11
- crystalwindow/gui.py,sha256=ApsavaunPEKufJFIXO1vEDsJpLqUSkMoqSajYAH1swU,2101
12
- crystalwindow/gui_ext.py,sha256=_91W9amoum0fJ8iHTdOoD8L5bS7n-4XBs-9J5HUb5dA,2274
13
- crystalwindow/player.py,sha256=4wpIdUZLTlRXV8fDRQ11yVJRbx_cv8Ekpn2y7pQGgAQ,3442
14
- crystalwindow/sprites.py,sha256=wH2AzOpCdaFX63JrW2mplKt3B3jnJdLG5maqgcWlR5w,901
15
- crystalwindow/tilemap.py,sha256=PHoKL1eWuNeHIf0w-Jh5MGdQGEgklVsxqqJOS-GNMKI,322
16
- crystalwindow/window.py,sha256=mnkVlKj1E7rsWqNHJknUMrPBq8rs3mgEXyOkdH3lgX4,17827
17
- crystalwindow-1.4.8.dist-info/METADATA,sha256=xMYs4R5hGlQEkvG3Bm-g-g0MLjBAtJtCeyfjxGulCJQ,556
18
- crystalwindow-1.4.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- crystalwindow-1.4.8.dist-info/top_level.txt,sha256=PeQSld4b19XWT-zvbYkvE2Xg8sakIMbDzSzSdOSRN8o,14
20
- crystalwindow-1.4.8.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- crystalwindow