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.
- crystalwindow-2.5.7.dist-info/METADATA +254 -0
- crystalwindow-2.5.7.dist-info/RECORD +4 -0
- crystalwindow-2.5.7.dist-info/top_level.txt +1 -0
- crystalwindow/FileHelper.py +0 -92
- crystalwindow/__init__.py +0 -47
- crystalwindow/animation.py +0 -15
- crystalwindow/assets.py +0 -33
- crystalwindow/collision.py +0 -15
- crystalwindow/draw_helpers.py +0 -23
- crystalwindow/draw_rects.py +0 -51
- crystalwindow/draw_text_helper.py +0 -45
- crystalwindow/fun_helpers.py +0 -28
- crystalwindow/gravity.py +0 -75
- crystalwindow/gui.py +0 -64
- crystalwindow/gui_ext.py +0 -62
- crystalwindow/player.py +0 -106
- crystalwindow/sprites.py +0 -30
- crystalwindow/tilemap.py +0 -13
- crystalwindow/window.py +0 -211
- crystalwindow-1.4.8.dist-info/METADATA +0 -19
- crystalwindow-1.4.8.dist-info/RECORD +0 -20
- crystalwindow-1.4.8.dist-info/top_level.txt +0 -1
- {crystalwindow-1.4.8.dist-info → crystalwindow-2.5.7.dist-info}/WHEEL +0 -0
|
@@ -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,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
crystalwindow/FileHelper.py
DELETED
|
@@ -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
|
-
]
|
crystalwindow/animation.py
DELETED
|
@@ -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)
|
crystalwindow/collision.py
DELETED
|
@@ -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
|
crystalwindow/draw_helpers.py
DELETED
|
@@ -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))
|
crystalwindow/draw_rects.py
DELETED
|
@@ -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
|
-
)
|
crystalwindow/fun_helpers.py
DELETED
|
@@ -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 = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAd80lEQVR4nO2dz2tbWZbHv1XSPCEhYcMTMjI2FuNCppuEBLwYkk0XU1C1Sa0Geuhdbbu3/Q8MTM+y1sPMqnrRDBR0byoMdEM1qaFJVh6qSOhgk4CCjIyNNKPMExJPSNQsTr2yYuvHe9I998d75wNCjqO8d2O/+73nnnPuOe8Bx99DuI3fBBofmh6FwMWrPwJv35gehXHeNz0AawkD0yMQuLh8LpP/B0QAFjHqmR6BwMGwC5w/Mz0KaxABWMR0bHoEgmrCADh7bHoUViECsIygY3oEgkpe/0mE/QYiAMuQhyU9tJ/Ktm4OIgDLGMoDkwq6p8DVC9OjsBIRgGWMJRLgPGEgTr8liAAsQ0KBbjMJZd+/AhGAZYgF4Dbnz2TfvwIRgGWMB6ZHIKzL5XOgd2Z6FNaTNz0A6wkDoFAxPYr18Dx65XJAqXT9vWp1vesNh8B0ev0+HgNhSF+PRurGvSmS7BMbEYBVjB0QgFwOKBZpkkfv0YRXSXTNyoKfRxAAnQ6JwXSq/v5xmISS7JMAEQAXKZdpMm5v04peKJgeEVGpAEdH9PVwSIIQBMDbt/rGIE6/RIgArCK4ACq7ZsewtUWTK1rh8w782iIrZGeH/tzvX7+4rIP2U2BwwXPtlOLAk5RBPI9W91rNntV9U7a36QXwiEG/Jck+SSnXRQBWMg313KdcpgkSrfRpJhKDyYRE4Px8MyEYdoHWE2XDSz1+E6gfA4WKCMBKuNKBczmaBNHEd8GsV00+TxGJahXodoFeDxgkDL1OQqD1jez7V+GVadJXj975dgafOoNEk37WHBaISAj6feDyMr4QSLLPcoo+sHsMbDfm/rUIwCpUrCxbWzTh142/Z4lIHPv965DiIiTZZzHlOk38FQ5sEYBVrLu65HKA76fLkaeTSAguL4GLi9s+gqAjyT7zKPpA7c4tU38RIgCq8TygXs/uvl41Ozv0s2y1rrcF0SEf4RqvDOw/XGjqL0KeUFVEE1/MfPUUCpRg1O1SxOCvj8XpF5HzgL0HsVf8m4gAbEpk6tfrsuJzU60ClRIwvAe8+LPp0Zgl5wG1u7TP3wB5YuOw6EBQsQgcHsoeXyeFEvDwH4HdI+DJF8DYokNIuqjdocmv4IzKe9IYJAbNR7e9qbUasL9vZjwCEQ6Bx58DvXPTI9FDuU7NahQeTpN6AEnJ5WjVl8lvnkIJePRr4OCe6ZHwUvRpETr6VPnJVBGAJORyQLMpSTw2USgBn/wKaD4wPRL1RA6+n/4D24E08QHEJZr8ac/Td5UPP6P3s5TkBtTuUOpunte/JAIQl8NDmfy28+FnQK/ttk+AYZ+/DNkCxGFvf3EVHMEuHv0aKPumR5Ecxn3+MkQAVuEVgf2m6VEIcSmUgIc/Nz2K+OQ8MvXnRZo0IFuAVXz8S3qoBHdo3KfIwJvvTI9kOX6TnHzM+/xliAWwjOYDSjgR3OPDz8h6sxGvDBx+THt9g5MfEAtgOcePTI9ALTfLegPXpb0Bymj0vNv/Lio8Olte3HYKJeD4U+DZl6ZHco2i9F2ViAAs4vgRUHHwYM9kQmfow5BOz0XvcYj7uVzuujJxqURf23gOovkAOPnKjnThrQM6rWdZiXkLf2uW4EpiyXBIr8GA3nU06JhOqdT3bLnvYpEiJVFdQxsolIAHPwe++a25MXhlMvVNV5ZegAjAPJoP7F39o0KaOid8HEYjel1d0ZbB9+ll+qBU4z5tA3RbAZF3328a3+cvQwRgHrbt/aMGG/2+PRN+GeMxVfG5uKByaDs75qyCQokEXefx4a0DKxx8cRABuMnBPTtW/yCgKrlBQBPKVaKtQrl8Xd1HN3c/0iMAa1blMYkIwE3ufmTu3kFANfBGI7cn/TwGA3qVy9ftw3RRqQL1JnDBVEDUEXN/HiIAs/h7ZuL+69bEd5HBAHj6n0DjUO/PuvmARwCKPtD4GVCywGpMQtABOiciAO/QfKj3fv0+0G6nb7VfRtCh7r0vQL6W40/13LdxX200IOfRPt8hcx/AjxM/6qEoAjCLrtDfcEgTPwsr/ixh8G4135PHQNCjUB13unXkDFRxXNiCFN7EDLvUO/FGHwURgIiDe3py/vt94PVr/vvYRlTK+2Y137NnVNrrk1/xj6FxfzMBsDymP5cwAC5OFjZQEQGIaNznv8dwSPXts8iyFl5vvqOMPe7tQH2DU52aCnQoYxLSz3xF56Q8aneotfI4Y+boLF4RONKw/2+31bXDdon209UtvJ5/zZ+AVSglPyVYrgP7D9xx8k1CMvWvnsfqnZDH/kOKXQYd6rX29o2GUVqGjtU/yt7LGt1TeiBXMR6RTyAq7cVF4348Adiw4YYRuqe06idomnK9Bajs0isMyCK4ep4dq0CHAFxe8t/DNoIO8Oab+J9vfUv+AE5fTJxogEOZfABuefaTcNsHUKgAO3fpFXTIdEtzB1avyC8ASU7kpYWbHv84jEckApzbsWXbANcy+SYh0HqykdW+3AkYWQV7D0gE0mgV6Fj9r67472ETizz+cTh5zO+P2T16VwCiTL6du7z3Vcnlc/Lub9gjMV4UIF9Ir1Wga/+fFSYhJfqs21Z90AO6baDK2Hilce+6UIil5/QXMuwCrW/W//neIHkYcNYq6LdIhVy1CnSY/64f5knKsnBfXC7OeAWgUgW26kD1rlvm/sVJPIdqAtbPA8gXyENaPXLXKtgkLhyXLK3+l8/VPANnT/kPZf3dZ3T+wgWCDu31GRZaNYlAs1bB1Qugd+qGVSDmvzqiEJQKeuf824By2X4BCAPKoWAMzavNBMwXqODh7vF1/FfRXoUFbgEYDrNh/g+7ycJ9cWDfBli+51fk5FsFX1nw6hE1NWw+omwq2/D3+HP/g4D3+jYw7JLTTzVnT9Vfc5ZFFZBNMwmBV39MnNCzLvxnASq7wNHuykMJ2tFh/qddACKPP8eD2jsHgi5vanClYtc2oHuq3pJagb7GIIUKZVfd+QXFXHOG1VeHAzDNyT+ckz+ixdzZp1zmvX5chl3g9Cvtkx8w0RmoUCEfgWkh4K5GM9t8I428/hO/f6dzynt9001OJiGl8L78w1ppvCowdxz4psNQZz6BjtU/zc6/1hM9Dyx3bz+TAsAY2kuCHfUAonyCfovCHtw/FH+P9/oAWQBppKPZj9M55bXWymW9WzUNob0k2CEAEdsNevVbFAbhWmV8xvBSRBoFILLUdHJxxisApZI+AVjjuC43dglARCQEGxxzXErFV3u9eaRt/2/AQ033bfNeX0coMOgAbQUp0gzYKQARsyFElftOHRZAmgSAI9EnLr1z3utz+gFiluUyid0CEFGoAEefqts/6Sj+6UILrzhwJfrEwSsD/h3qh8jVfZjLAtDlz9oQNwQgolABPvhks8NHOiIAaSGa/Lr3rDfLcQ0GfC3FVDcvVW2tMuOWAEREh4/qx8ktAq/IN66IMOS/BzeTkM6d65z8Xhmo3b3dYms45O0pqCISEBXj1O0k3RA3BSAisgiSpBlzHjCJcD0FeNOiHknJeTTxd4/n/73tERVHzP15uC0AEVGacf14tRDocAC6fARY5+RftOLfhNuhum4o0DFzfx7pEICIOEJQ1hACdPkMgI4U36JPjTbiltzm/nnmcsn/TefEOXN/HukSgIhlQsC9BQgCd0OAr/7Iu5qV61RX0pUyXPOwJIVXFekUgIhICGp3SLGnGn5pLpv/H3xC78MuOf+GvR/eu2QVrPvQF33qrrNJT70g4CviUakAFyuELwXm/jzSLQARpSo93Fsajn+67gAErttgLZqwk5AEIRKJiHFAE2X2z/Vjt7rrzCMl5v48siEAERXGUBJA4b+0JAAtI1+4FgeXzflVOOzdj0u2BID7+KfL5r/t6PSrpNTcn0e2BKDInAQkAsAHdzIQwFZ732ayIwCex5dPDlC+usvhv6yjuyiNJWRHAGT1d5t1YvVxSdrFOEXorwloCu468LL682K6fl9KyY4AcD9AaQj/ZRXumgMWkx0B4LQAwjDdRUDTztjyw0aMZEMAuMs+ZSH2bxrbW3k5igiACmw/ruo63L8/7rqDFpMNAVBd9eUmsv/nhVsAxtm14LIhANwPkKun/1yB2/zviQUgbIL4AHjhzgAUC0AQLCWX4w3hZnj/D4gACLbDvfoP7GvWoZPspAILbuIzl3ALp1SzIKNkQwC4nXS6G0xmBc/TEP8vL65GnAGysQXgjtNzHlTJMvU6/z0yLtzZEAAdZaUFtXgeUK3y3kPyNzIiANxhOm5HVRbZlwYuOsiGAAC8v+xSSbYBKimX9Yiq1HDIkABw+wG4vdVZIZcDDg/575OVAq4ryI4AcJt7tRrv9bPC4SFv6bYIWf0BZEkA3r7lvX6hQKarsD57e/qO/faynQAUkR0BAPhVf3eDzjdZx/eBnR099woCMf9/IFsCwL0NqFSArS3ee6QR3wcaDX33k9X/R7IlADr2fY2GRASSoHvyTyYiADNkSwDGY34RyOf1PtAuo3vyA8DVld77WU62BADQYwVsb0tYcBUHB/on/2QiAnCD7AlAr0cPAjd7e/zNSFwklwN+8hP+NN95XF1J9aYbZE8AAD17wHweaDZFBGYpl2nymzg7Iav/XEQAOBERuGZvDzg64i/Quojzc1n955BNARiN9B0EiUQgq+FBz6P/v64Y/zyCQDz/C8imAAB6H4h8HvjgAz3n223C94G7d8039Whnu+7fMrItAGGo9567u+T9TnueQLTq2xAObbcl628J2RUAALi40H/PajXdfoF63Y5VH6CQrzj+lpJtATBhBQDkBf/pT8kxlhZroFgkYbPlPEQYAq2W6VFYT7YFADBjBUTs7FBYzHUHYa1GgmbDqh/RaonXPwbZqAq8jF6PHmBTdf0KBXIQBoF7+9WtLVrxbauJ2G5nvthnXMQCAOzwElcqtIoeHvL3MtyUrS0y9z/4wL7J3+nIvj8BYgEAtFp0u2bSU2+yvU2vIAAuL/kLmcTF82hctZq5ZJ5VdLtmt3QOIgIQcX5OD7iOclRxqFToFYa0ovX7dJpRJ1FjDt+3a38/j24XePPG9Cic4z385F++x3SNB8urAAXLH4qkbG2RWWsrQUBCMByq3+PmcuTJLxTovVKxz7xfxHAIvHxpehRO8h5w/P1GV8h5QNEHSj6JwnbDbWE4PHSnzn+Uzjydrlf1uFLh777Ljaz8G7G5AMwj55EQFH33BCGXIweXy5MiK8jk3xgeAbjJ1gGw/9AdIYiSWmzxBwi3abXkgI8C9AhAhFcGaneBnbvabrk2tvsDsoxMfmXoFYCInAf4TbIKbMZEzTphMZMJ8Pq1JPkoxEwi0HQMXL0wcutE9HqST24LwyFwdiaTXzGyyV1Fr0cPn/gEzNHtSkUfJsymAgcdo7ePzWhEq4+Jk4NZZjIBXr0iT79MfhbkLEBcRiPgxQtKzxX4CUMSXVtSoVOK2LRJOT8nMdjbky0BF2Lya0Oe4HXo9SgLr9GwP0feJSYTcrrKqq8NswIQBoCr82c8JhPV98UaUEG/L0U8DGD2qR2nIKQTlRU7OjI9EjeRVd8osmxtiufRASIhOVFsX1Z9Y5iNAgy7Rm+/MbkcTX4x/9ejVEpPUVRHMSsA69QhsIm9PTk1uClZa5ZiGWYFYOBw+aaDAztKiLlOtUpNQwUjmE8ECjX16FNJrSaTXyUm+wZmHPMCMHZMAIpFYH/f9CjSxfa2WAGGMC8AgUPbgKhQiKAesQKMYF4AXIkE5HKU+Scefx62t9PbL9FizAuAK8lAh4fi8eemVjM9gsxhXgBGDpR2OjjIXs7/ZHJdhrzTua5AzEm1an9XpJRhhz0bdICKJV1lb2LS4x/Vu/c8et0s4b1uSe+bZcTH4+taB+Px/AYkw6EeEazXpdKvRiwRgAs7BaBcNufxj3LkgXcnpamceV1NS6tVOQqsEfNbAMBOR6DJHP/JhHLkbeoUPB6v13xkHcQXoA07BMC2jEDTOf5R0RHb0FWKWwRAG3YIwHRsV0agSY//q1f21rzv9cg64SafpzoLAjt2CABgTzSgVjPj8XfhXPx0Sp2KdSBWgBbsEQAbKgRvbZlz+rnS7ebqSo8VUCpJerAG7BGAoeGHv1g01wXI9pV/lumUfBQ6kPRgduwRAJOOQJNOP1dW/lmiZincbG9LYhAz9ggAYC4c2GgAhYL++7bb7k3+CF0t08QZyIplAmBgMuzt0Uqjm05Hn0ONg9GI0oS5EQFgxS4B0B0J2Noys8/sdoELy3If1qHd5r9HoUC/J4EFuwRApwVgyuk3HKYn1308JjHjRqovsWGXAOhyBJo62x+VwU4TOiyZ7W2pHsyEXQIA6HEEmqjmGyX6pO2Qiy4rwISfJgNYKADM2wDf129S2ni4RyU6rADJDGTBPgHgdAQWi7T668bWwz2qGI/5IwKlkuQEMGCfAHAdCjKV7HN56W6sPwmXl/z3kG2AcuwTAK7CIHt7+pN9hkN9abOmGQyuqwpxITkByrFPAHyGstsmynqFIfD6td57mobbFyDbAOXYJQBFH8grXqVNNfJ4/Xp+bb000+/znxTMWnFWZuwSgO2G2utF8X7dtNvpdvotYjqlrQAn4gdQimUCcKD2evW6/nh/t+t2jv+mcOcEiAAoxR4B8MpASeE+vVjUn+cfhtlx+i3i7Vv+bYAUClGGPQKg2vtvwvR//Tp9mX7rwJ0TIH4AZdgjACr3/76v3/TP6r5/HtwCINsAZdjRGCTnqRWAel3dteISNbccDGgCZNkS4C5vJj0alWGHAKic/OWymeo+lQq9onyDICCfQFYFod/nXanLZf6IQwZInwDYki12UxAmE3pgh0N6uVIEdF2CgFcAKhURAAXYIQBlhSa7rQ6ifJ4mxOykmEzIbzAckoUQBIubc9pOLkdboKhhKXfGnmwDlGBeAMp1ddl/uZwZ839d8vlrS+Emw+G7/fgikZhOzTobcwBqPwh2NG4ToispwUowLwAqzf9iUd21TFMq0WuZGR2JwqrvJWFRy3HbLCuxAJSQLgHIGi5MVE7EEbgxZvMAij5QyNADK6hF6gRujFkBUH30VxJxsoVsAzbGrABUFCfsTKf8RSkEexALYGPMCYDqwz8ROrrVCHYgFsDGmBMALudflo/iCkJCzAkAR+kvQF+desE8nifbgA0xIwBc5n/E+Tn/mXTBPIWCmYNfKcKMANTu8l5/OnUznVZIzs5OuhLANGNGALiTf0y0/hLMsctUSj4D6BeArQPe5B9TLb8Fc2xvy9mANdEvAJyrv6kqwIJ5pErQWqRLAEy0/hLswJY6EI6hVwC2DtQ3/vjx2lvZOggjvEupJM7ANdArANUjvmub6P4j2IVYAYnRJwCqC3/OUq+7VQhE4EH8AInRJwBcmX+5HDX/FIRCQbYBCdEnAFzJP7Wafsdft00vwT7ED5QIPTOHq/CHidX/9CnwzW+v/1xvAv4eUKnS+y6jn0NYTaUiB8ISoEcAaneYrqt59X/+NfDsy3e/d3FGr1n8PeDOR8DRQ31jEwjpG5gI/tnjlXm8/7pX/2739uRfRO8cGPR4x+MCQRcIevTzGA+BzhngFYFPfsV3z3yesgLlLEgs+AXAZzKJfV/f6t/tAq/PVn9uliBjAnDyFRAOabIHveUCePqU1zoqFkUAYsI7g3Ier/mvg34fePMGGCWc0JwCEHSBs2e3v1/2gcqSWHjFJ18FB505W6FFtL7lFYBSKf2dlxTBKwDbDZ7MP9/XE/cPQ6DV4r9PUoIecPI4+b/7+Jd8ApCEN9/xXl9OgsaGNwxYP+a5rq6Mr1YrPU09yz7QuM93fX8v2ec7pzzjAKRKUAL4BMBv8oT+ikU9sd7Ly3ebTgQX/PfkpHGP9/qFhKtu75xnHIDkAiSATwC4Vn8de//JBLhwfMLfpMkckgyHyT4/Tvh5gQUeAajd4Uv80ZHvfXWVHtMfIPO/ynxYKumKzp1JKfkAsVAvADmPb/Xf3uYP/S1a/acONxzZZTqHMUvSFX0sXZxsQL0A1I/5zvzrMP8XpZEOHY7rczr/IpJaAHFDhgIragWg6AM7TId+PI8/vDOZpDOPvM5sAbS+5b3+OogjMBZqBWD/gdLLvYOO0F+/n669P0DhuaQe+qSsKwCcoUAhFuoEwG8CFcbyzDoEIG2ef0CP+W+jBSDEQo0AcDr+AIr9c2f+9fvpzB/XYf6v69ATR6Bx1AjA3gPeWv869nOr+gkOHLUOuOsTbLL696Soimk2F4BynbfYJ8Bv/odhOg+PJE3PXQcx/51mcwHgdPxFcHv/+33e65uC2/w/fSpmvONsJgD1Y94uv4CeIo89h2P8y/CZs/8klu886wtA0Qd2GR1/Edwnu8IQGKV0FVtWG0AFYv47z/oC0PiZwmEYxEXzP26xEU4HYOdUzP8UsJ4A6DD9deGi+R+n3mCZefUX8z8VJBcAXaa/DsT8Xx/b+yKkMaeDgWQCkPOAw4+ZhrIAzl9kEPBdmxMvRlSEWwBstwBCh09vaiSZANSPeRN+5jEe8/0yXU39jRPf5xSAcGj//l8sgFjEF4Byne+k3yo4TuilNfVXB6oy+LgqJ08m8ruNSTwBMGH6z9LrqbUCJhOgbfkedlM4cwBUTVyuMOJsLUdhKfEE4PBjviIfcZhO1ZbnPj9P/wrhMSZQqep6NB7xiICLoV1DrBaA2h3eY75xGQzUiEC77WboL608/1rt9cIh0LtUe80Us1wAij6wb1GDy15vMxHodNb3J+S89e8rLObiTG1hkBdfA62/qLteylksAKb3/Yvo9YC//jWZTyAMgVevNvP6FzU1I8kiT36bvKz4PFrfUsek3hnQb21+vQywWAAaH+oP+cVlNAJeviRzfpkQDIf0mZcv9R739YrA3Y/03c91Bj3g8eebiUC3DTz54vrPrSfARHIBVjG/xrbfpL5+NjOdkjl/dUUFQ70bJroJT3C9SRNfRxmutNE7B37/G+pfmLSHwclXt3slTsfA+TNayISF3BaAok8VflxiPDbr1feK9OByV9+xBa5zBoMe8IffAHf+Hjj+dHUx05OvgNNni6MSvTNayGxfzAzyrgDkPDrlZzLkZyuLnID+nj1dd3Vx9JAm55MveDICX/yZXs0HZE3NhjTHI3Iatr6LF45sPQHu/EKe6QW8KwCND9Nzyk81pSrw9s273/P3gEe/5i+7bSON+/R/f/w5X1rw2TN6bcJ0DFyc2BXNsohrJ6AL+37b+Nln9k5+zu67EdV9EgHOpCMVXL0AhiuKvmYUEgAX9/2mOX7E33BzE3R1363u037ddlrfmB6Blbwv+/6YVOrXX3tFNx56Xdz9iL8AyaaMekDnxPQorON92fevQVOspVu4kPdw9RwIHa0BwcT7su+PiTeTFCVx/ts07pkewWqmY6D91PQorEJ9e/C0MpsVmZV4fxIqVfu3AQBFcoKO6VFYgwhAEuRA0HK4y5CpQnwBPyICkARbDgTFmWi2F+00yeBCDgv9gAhAWrG9Zp9pxBcAQAQgGVFhFNOra5ySXCY673LV+ONgPAC6CusQOIoIwDqYbmsdZ3WPcuZ1EQ7VlQrTxYX4AkQAkhAlA5muiR93Yqsut7UMF/sEjgeZ9wWIACQhigKcPVNTwWZdWt/F+9yb7/RNzJvn8V3h8rnpERhFBCAJsxmTf/pXM2NofZvM1H7yBb/P4vnX7pn/EYOLTOcFiAAkxSvT+8WZXhM74umXyT4/HtGRXS4R6LapMIfL9Cxvc8aICEBSZlOCn32pNyJw8tV6K+14RJV2VE/UoMtbD0AXvbPM1g8UAUjKzR4Jjz/X420/fbr5PvvkMfD7f1YjWt02bYNcn/wRVy9Mj8AIOex++k+mB+EU0zHwv69n/jy5dgrW/hbI/43a+4VD4C+/A/5bkZNt9H/Ay/+iryv+egVNnn8NfPMFMPgfNWOygXFgrvelQd7D8b99b3oQThEGwIv/WPz39SYVC9n0wFC3DZw9JXHhXGXrTTrd6O9RP8F5ghB0Kcmn9W38Wnwucvhx5qpiiQCsw8m/r/6MV6SJVfHplNyq/P0ocad3TpMtrZPMZvxm5sqIz+8LICynXKfw0TLGo80LWgp66bfIGZih6ljiBFwHG5qlCuqZjjOXGSgCsA4lS44FC+oRARBWUq6v/ozgJjd7P6QcEYB1yBfsKQ4iqCdDVoAIwLpUxApILSIAwkrEEZheMnQ4SARgXcQPkF7Gg8y0EhMBWJd8Adg6MD0KgYtgRZ5HSvh/St3ukH8llrUAAAAASUVORK5CYII="
|
|
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
|
|
File without changes
|