Skip to content

Commit f8e718a

Browse files
authored
Update main.py
1 parent 2a7eaa2 commit f8e718a

File tree

1 file changed

+216
-46
lines changed

1 file changed

+216
-46
lines changed

app/main.py

Lines changed: 216 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,218 @@
1-
const { app, BrowserWindow, ipcMain } = require('electron');
2-
const { exec } = require('child_process');
3-
const path = require('path');
4-
5-
function createWindow() {
6-
const win = new BrowserWindow({
7-
width: 1280,
8-
height: 900,
9-
webPreferences: {
10-
nodeIntegration: true,
11-
contextIsolation: false
12-
},
13-
icon: path.join(__dirname, 'images/hackeros.png')
14-
});
15-
16-
win.loadFile('index.html');
17-
win.setMenuBarVisibility(false);
1+
import sys
2+
import os
3+
import subprocess
4+
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QGridLayout, QMainWindow
5+
from PySide6.QtGui import QFont, QPixmap, QIcon, QFontDatabase, QLinearGradient, QBrush, QColor, QPainter, QPalette, QMovie
6+
from PySide6.QtCore import Qt, QPropertyAnimation, QSequentialAnimationGroup, QRect, QTimer, QAbstractAnimation, QEasingCurve, QUrl, QPoint, QElapsedTimer
7+
8+
# Use system icons directory
9+
IMAGES_DIR = '/usr/share/HackerOS/ICONS'
10+
11+
# Game paths and commands (hardcoded as in original)
12+
GAME_PATHS = {
13+
'starblaster': '/usr/share/HackerOS/Scripts/HackerOS-Games/starblaster',
14+
'bit-jump': '/usr/share/HackerOS/Scripts/HackerOS-Games/bit-jump.love',
15+
'the-racer': '/usr/share/HackerOS/Scripts/HackerOS-Games/the-racer'
1816
}
17+
LAUNCH_COMMANDS = {
18+
'starblaster': [GAME_PATHS['starblaster']],
19+
'bit-jump': ['love', GAME_PATHS['bit-jump']],
20+
'the-racer': [GAME_PATHS['the-racer']]
21+
}
22+
23+
class ParticleWidget(QWidget):
24+
def __init__(self, parent=None):
25+
super().__init__(parent)
26+
self.setAttribute(Qt.WA_TransparentForMouseEvents)
27+
self.particles = [
28+
{'cx_start': 0.10, 'cx_end': 0.15, 'cx_dur': 4, 'cy_start': 0.15, 'cy_end': 0.85, 'cy_dur': 4, 'r': 5, 'color': QColor(57, 255, 20, 178)},
29+
{'cx_start': 0.30, 'cx_end': 0.35, 'cx_dur': 5, 'cy_start': 0.50, 'cy_end': 0.95, 'cy_dur': 5, 'r': 4, 'color': QColor(0, 183, 235, 178)},
30+
{'cx_start': 0.60, 'cx_end': 0.55, 'cx_dur': 4.5, 'cy_start': 0.25, 'cy_end': 0.75, 'cy_dur': 4.5, 'r': 6, 'color': QColor(255, 7, 58, 178)},
31+
{'cx_start': 0.80, 'cx_end': 0.85, 'cx_dur': 5.5, 'cy_start': 0.40, 'cy_end': 0.90, 'cy_dur': 5.5, 'r': 3, 'color': QColor(255, 255, 255, 153)},
32+
{'cx_start': 0.20, 'cx_end': 0.25, 'cx_dur': 4.8, 'cy_start': 0.70, 'cy_end': 0.20, 'cy_dur': 4.8, 'r': 4, 'color': QColor(57, 255, 20, 178)}
33+
]
34+
self.elapsed = QElapsedTimer()
35+
self.elapsed.start()
36+
self.timer = QTimer(self)
37+
self.timer.timeout.connect(self.update)
38+
self.timer.start(16) # ~60 FPS
39+
40+
def paintEvent(self, event):
41+
painter = QPainter(self)
42+
painter.setRenderHint(QPainter.Antialiasing)
43+
w, h = self.width(), self.height()
44+
for p in self.particles:
45+
# cx
46+
dur_cx = p['cx_dur'] * 1000
47+
t_cx = self.elapsed.elapsed() % dur_cx
48+
frac_cx = t_cx / dur_cx
49+
cx = p['cx_start'] + frac_cx * (p['cx_end'] - p['cx_start'])
50+
cx_abs = cx * w
51+
# cy
52+
dur_cy = p['cy_dur'] * 1000
53+
t_cy = self.elapsed.elapsed() % dur_cy
54+
frac_cy = t_cy / dur_cy
55+
cy = p['cy_start'] + frac_cy * (p['cy_end'] - p['cy_start'])
56+
cy_abs = cy * h
57+
painter.setBrush(QBrush(p['color']))
58+
painter.setPen(Qt.NoPen)
59+
painter.drawEllipse(int(cx_abs - p['r']), int(cy_abs - p['r']), int(p['r']*2), int(p['r']*2))
60+
61+
class GameCard(QWidget):
62+
def __init__(self, game_name, image_file, color_class, parent=None):
63+
super().__init__(parent)
64+
self.game_name = game_name
65+
layout = QVBoxLayout(self)
66+
layout.setContentsMargins(40, 40, 40, 40) # p-10
67+
68+
# Image
69+
self.image_label = QLabel()
70+
image_path = os.path.join(IMAGES_DIR, image_file)
71+
pixmap = QPixmap(image_path)
72+
self.image_label.setPixmap(pixmap.scaledToHeight(320, Qt.SmoothTransformation)) # h-80 approximate
73+
self.image_label.setAlignment(Qt.AlignCenter)
74+
layout.addWidget(self.image_label)
75+
76+
# Animate pixel-bounce
77+
self.bounce_anim = QPropertyAnimation(self.image_label, b'pos')
78+
self.bounce_anim.setDuration(1400)
79+
self.bounce_anim.setLoopCount(-1)
80+
self.bounce_anim.setEasingCurve(QEasingCurve.InOutSine)
81+
start_pos = self.image_label.pos()
82+
self.bounce_anim.setKeyValueAt(0, start_pos)
83+
self.bounce_anim.setKeyValueAt(0.5, start_pos + QPoint(0, -25))
84+
self.bounce_anim.setKeyValueAt(1, start_pos)
85+
self.bounce_anim.start()
86+
87+
# Title
88+
title = QLabel(game_name.replace('-', ' ').title())
89+
title.setFont(QFont('Press Start 2P', 24)) # text-4xl
90+
title.setAlignment(Qt.AlignCenter)
91+
title.setStyleSheet("color: white;")
92+
layout.addWidget(title)
93+
94+
# Button
95+
button = QPushButton('Launch')
96+
button.setFont(QFont('Press Start 2P', 12))
97+
neon_color = self.get_neon_color(color_class)
98+
dark_color = self.get_dark_color(color_class)
99+
button.setStyleSheet(f"""
100+
QPushButton {{
101+
background-color: {neon_color};
102+
color: black;
103+
padding: 16px;
104+
border-radius: 8px;
105+
}}
106+
QPushButton:hover {{
107+
background-color: {dark_color};
108+
}}
109+
""")
110+
button.clicked.connect(self.launch_game)
111+
layout.addWidget(button)
112+
113+
# Card style
114+
shadow_color = self.get_shadow_color(color_class)
115+
self.setStyleSheet(f"""
116+
background-color: #1a2533;
117+
border-radius: 16px;
118+
border: 4px solid #ffffff55;
119+
box-shadow: 0 0 30px rgba(255, 255, 255, 0.5), {shadow_color};
120+
""")
121+
122+
def get_neon_color(self, color_class):
123+
if color_class == 'green': return '#39ff14'
124+
if color_class == 'blue': return '#00b7eb'
125+
if color_class == 'red': return '#ff073a'
126+
return '#39ff14'
127+
128+
def get_dark_color(self, color_class):
129+
if color_class == 'green': return '#2ecc40'
130+
if color_class == 'blue': return '#0099c9'
131+
if color_class == 'red': return '#d90429'
132+
return '#2ecc40'
133+
134+
def get_shadow_color(self, color_class):
135+
if color_class == 'green': return '0 0 40px rgba(57, 255, 20, 0.7), 0 0 80px rgba(57, 255, 20, 0.5)'
136+
if color_class == 'blue': return '0 0 40px rgba(0, 183, 235, 0.7), 0 0 80px rgba(0, 183, 235, 0.5)'
137+
if color_class == 'red': return '0 0 40px rgba(255, 7, 58, 0.7), 0 0 80px rgba(255, 7, 58, 0.5)'
138+
return '0 0 40px rgba(57, 255, 20, 0.7), 0 0 80px rgba(57, 255, 20, 0.5)'
139+
140+
def launch_game(self):
141+
command = LAUNCH_COMMANDS.get(self.game_name)
142+
if command:
143+
try:
144+
subprocess.Popen(command)
145+
except Exception as e:
146+
print(f"Error launching {self.game_name}: {e}")
147+
148+
class MainWindow(QMainWindow):
149+
def __init__(self):
150+
super().__init__()
151+
self.setWindowTitle("HackerOS Games")
152+
self.setGeometry(100, 100, 1280, 900)
153+
self.setWindowIcon(QIcon(os.path.join(IMAGES_DIR, 'HackerOS-Games.png')))
154+
155+
# Central widget
156+
central = QWidget()
157+
self.setCentralWidget(central)
158+
main_layout = QVBoxLayout(central)
159+
main_layout.setContentsMargins(40, 40, 40, 40) # p-10
160+
161+
# Background gradient
162+
palette = central.palette()
163+
gradient = QLinearGradient(0, 0, self.width(), self.height())
164+
gradient.setColorAt(0, QColor(8, 12, 20))
165+
gradient.setColorAt(1, QColor(22, 32, 47))
166+
palette.setBrush(QPalette.Window, QBrush(gradient))
167+
central.setPalette(palette)
168+
central.setAutoFillBackground(True)
169+
170+
# Title
171+
title = QLabel("HackerOS Games")
172+
title.setFont(QFont('Press Start 2P', 48)) # text-6xl
173+
title.setAlignment(Qt.AlignCenter)
174+
title.setStyleSheet("color: #39ff14; text-shadow: 0 0 10px #39ff14, 0 0 25px #39ff14, 0 0 50px #39ff14;")
175+
main_layout.addWidget(title)
176+
177+
# Grid for games
178+
grid = QGridLayout()
179+
grid.setSpacing(48) # gap-12
180+
181+
# Starblaster
182+
starblaster = GameCard('starblaster', 'starblaster.png', 'green')
183+
grid.addWidget(starblaster, 0, 0)
184+
185+
# Bit-Jump
186+
bitjump = GameCard('bit-jump', 'Bit-Jump.png', 'blue')
187+
grid.addWidget(bitjump, 0, 1)
188+
189+
# The-Racer
190+
racer = GameCard('the-racer', 'The-Racer.png', 'red')
191+
grid.addWidget(racer, 0, 2)
192+
193+
main_layout.addLayout(grid)
194+
195+
# Logo
196+
self.logo = QLabel(central)
197+
logo_pixmap = QPixmap(os.path.join(IMAGES_DIR, 'HackerOS-Games.png')).scaled(80, 80, Qt.KeepAspectRatio)
198+
self.logo.setPixmap(logo_pixmap)
199+
self.logo.setFixedSize(80, 80)
200+
self.logo.move(self.width() - 100, 24) # top-6 right-6
201+
self.logo.raise_()
202+
203+
# Particles
204+
self.particles = ParticleWidget(central)
205+
self.particles.setGeometry(0, 0, self.width(), self.height())
206+
self.particles.lower()
207+
208+
def resizeEvent(self, event):
209+
self.particles.setGeometry(0, 0, self.width(), self.height())
210+
self.logo.move(self.width() - 100, 24)
211+
super().resizeEvent(event)
19212

20-
app.whenReady().then(() => {
21-
createWindow();
22-
23-
app.on('activate', () => {
24-
if (BrowserWindow.getAllWindows().length === 0) {
25-
createWindow();
26-
}
27-
});
28-
});
29-
30-
app.on('window-all-closed', () => {
31-
if (process.platform !== 'darwin') {
32-
app.quit();
33-
}
34-
});
35-
36-
ipcMain.on('launch-game', (event, command) => {
37-
exec(command, (error, stdout, stderr) => {
38-
if (error) {
39-
console.error(`Error launching game: ${error.message}`);
40-
return;
41-
}
42-
if (stderr) {
43-
console.error(`Stderr: ${stderr}`);
44-
return;
45-
}
46-
console.log(`Game launched: ${stdout}`);
47-
});
48-
});
213+
if __name__ == '__main__':
214+
app = QApplication(sys.argv)
215+
# Assume 'Press Start 2P' font is installed system-wide; no need to load from file
216+
window = MainWindow()
217+
window.show()
218+
sys.exit(app.exec())

0 commit comments

Comments
 (0)