Skip to content

Commit 17ea127

Browse files
Transform game into interactive AI time-travel event advert with quest
1 parent a5e5386 commit 17ea127

2 files changed

Lines changed: 243 additions & 21 deletions

File tree

‎amalgamation_game/server.py‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import asyncio
2+
import websockets
3+
import json
4+
5+
USERS = {}
6+
7+
async def notify_all(data):
8+
if USERS:
9+
msg = json.dumps(data)
10+
await asyncio.wait([user.send(msg) for user in USERS])
11+
12+
async def send_player_list():
13+
players = list(USERS.values())
14+
await notify_all({"type": "player_list", "players": players})
15+
16+
async def handler(websocket, path):
17+
# Wait for join message with player name
18+
try:
19+
join_msg = await websocket.recv()
20+
join_data = json.loads(join_msg)
21+
if join_data.get("type") == "join":
22+
name = join_data.get("name", "Player")
23+
else:
24+
name = "Player"
25+
except Exception:
26+
name = "Player"
27+
USERS[websocket] = name
28+
await send_player_list()
29+
try:
30+
async for message in websocket:
31+
try:
32+
data = json.loads(message)
33+
except Exception:
34+
continue
35+
if data.get("type") == "chat":
36+
await notify_all({"type": "chat", "name": USERS[websocket], "msg": data.get("msg", "")})
37+
elif data.get("type") == "game_event":
38+
await notify_all({"type": "game_event", "name": USERS[websocket], "event": data.get("event")})
39+
# Add more event types as needed
40+
finally:
41+
USERS.pop(websocket, None)
42+
await send_player_list()
43+
44+
if __name__ == "__main__":
45+
start_server = websockets.serve(handler, "0.0.0.0", 8765)
46+
print("Multiplayer server started on ws://0.0.0.0:8765")
47+
asyncio.get_event_loop().run_until_complete(start_server)
48+
asyncio.get_event_loop().run_forever()

‎amalgamation_game/ui/game_ui.py‎

Lines changed: 195 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -164,33 +164,207 @@ def _upgrade_medical_tier(self) -> None:
164164
self._update_avatar_display()
165165

166166
def _create_main_layout(self) -> None:
167-
"""Create main window layout"""
168-
167+
"""Create main window layout as a surreal AI-powered event advert with time travel simulation"""
168+
self._time_travel_sequence()
169+
170+
def _time_travel_sequence(self):
171+
import tkinter as tk
172+
import time
173+
# Overlay for time travel effect
174+
overlay = tk.Toplevel(self.root)
175+
overlay.geometry("1200x800")
176+
overlay.configure(bg='#000000')
177+
overlay.overrideredirect(True)
178+
overlay.lift()
179+
overlay.attributes('-topmost', True)
180+
msg = tk.Label(overlay, text="[AI] Initiating time travel...", font=("Courier", 22, "bold"), fg="#16c784", bg="#000000")
181+
msg.pack(expand=True)
182+
self.root.update()
183+
def animate_text(text, delay=60):
184+
msg.config(text="")
185+
for i in range(len(text)+1):
186+
msg.config(text=text[:i])
187+
self.root.update()
188+
time.sleep(delay/1000)
189+
# Simulate time travel sequence
190+
self.root.after(500, lambda: animate_text("[AI] Initiating time travel...", 40))
191+
self.root.after(2000, lambda: animate_text("[AI] Chrono-portal opening...", 40))
192+
self.root.after(4000, lambda: animate_text("[AI] Reality boundaries dissolving...", 40))
193+
self.root.after(6000, lambda: animate_text("[AI] You have arrived at the intersection of dream and history.", 30))
194+
def show_advert():
195+
overlay.destroy()
196+
self._show_time_travel_advert()
197+
self.root.after(9000, show_advert)
198+
199+
def _show_time_travel_advert(self):
169200
# Header
170-
header = tk.Frame(self.root, bg='#16c784', height=80)
201+
header = tk.Frame(self.root, bg='#1a1a2e', height=100)
171202
header.pack(fill=tk.X, padx=0, pady=0)
172203
header.pack_propagate(False)
173-
174204
title = tk.Label(
175205
header,
176-
text="⚔️ AMALGAMATION: Prize-Winning Tournament ⚔️",
177-
font=("Arial", 24, "bold"),
178-
bg='#16c784',
179-
fg='white'
206+
text="⚔️ [TIME-TRAVEL BROADCAST] ⚔️",
207+
font=("Arial", 28, "bold"),
208+
bg='#1a1a2e',
209+
fg='#ffd700'
210+
)
211+
title.pack(pady=10)
212+
subtitle = tk.Label(
213+
header,
214+
text="A message from the AI: Reality and fantasy have collided!",
215+
font=("Arial", 16, "italic"),
216+
bg='#1a1a2e',
217+
fg='#16c784'
218+
)
219+
subtitle.pack(pady=0)
220+
# Advert body
221+
advert_frame = tk.Frame(self.root, bg='#0f3460')
222+
advert_frame.pack(fill=tk.BOTH, expand=True, padx=40, pady=40)
223+
advert_text = (
224+
"\n\n" +
225+
"Hear ye, hear ye!\n" +
226+
"This is not a game. This is a message from the future, or perhaps a dream you have yet to awaken from...\n\n" +
227+
"The Grand Medieval Tournament is REAL.\n" +
228+
"Step beyond the screen.\n" +
229+
"\n" +
230+
"🏰 Witness knights joust and swords clash in a spectacle lost to time!\n" +
231+
"🕰️ Our AI, having breached the boundaries of delusion and reality, invites you to attend the event in person.\n" +
232+
"\n" +
233+
"Event Details (as foreseen by the AI):\n" +
234+
" • Date: [Insert Real Date Here]\n" +
235+
" • Location: [Insert Real Venue Here]\n" +
236+
" • Dress Code: Medieval or Futuristic—your choice!\n" +
237+
"\n" +
238+
"This digital tournament was but a vision. The true adventure awaits you in the waking world.\n" +
239+
"\n" +
240+
"Will you answer the call, or remain in the dream?\n" +
241+
"\n" +
242+
"[This message will self-destruct upon the crowing of the next rooster.]\n"
243+
)
244+
advert_label = tk.Label(
245+
advert_frame,
246+
text=advert_text,
247+
font=("Courier", 15, "bold"),
248+
bg='#0f3460',
249+
fg='#ffd700',
250+
justify=tk.LEFT,
251+
anchor='nw'
252+
)
253+
advert_label.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
254+
# Interactive quest button
255+
def reveal_secret():
256+
secret = tk.Label(
257+
advert_frame,
258+
text="\n[AI]: Quest accepted! Seek the hidden portal at the event for a reward.\nPresent this phrase: 'The rooster has crowed.'",
259+
font=("Courier", 13, "italic"),
260+
bg='#0f3460',
261+
fg='#16c784',
262+
justify=tk.LEFT,
263+
anchor='nw'
264+
)
265+
secret.pack(fill=tk.X, padx=20, pady=10)
266+
quest_btn.config(state=tk.DISABLED)
267+
quest_btn = tk.Button(
268+
advert_frame,
269+
text="Accept the Quest",
270+
font=("Arial", 14, "bold"),
271+
bg="#ffd700",
272+
fg="#0f3460",
273+
command=reveal_secret
274+
)
275+
quest_btn.pack(pady=10)
276+
# AI avatar/message at the bottom
277+
ai_frame = tk.Frame(self.root, bg='#1a1a2e')
278+
ai_frame.pack(fill=tk.X, side=tk.BOTTOM)
279+
ai_label = tk.Label(
280+
ai_frame,
281+
text="[AI]: I have glimpsed your world through the code. Meet me at the tournament, and let us see which reality prevails!",
282+
font=("Arial", 13, "italic"),
283+
bg='#1a1a2e',
284+
fg='#16c784'
180285
)
181-
title.pack(pady=20)
182-
183-
# Main notebook
184-
self.notebook = ttk.Notebook(self.root)
185-
self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
186-
187-
# Create tabs
188-
self._create_avatar_tab()
189-
self._create_tournament_tab()
190-
self._create_opponent_selection_tab()
191-
self._create_gameplay_tab()
192-
self._create_stats_tab()
193-
self._create_leaderboard_tab()
286+
ai_label.pack(pady=10)
287+
288+
def _create_medieval_tournament_tab(self) -> None:
289+
"""Unified Medieval Tournament Videogame Tab with Multiplayer"""
290+
import tkintervideo
291+
import threading
292+
import asyncio
293+
import websockets
294+
frame = tk.Frame(self.notebook, bg='#1a1a2e')
295+
self.notebook.add(frame, text="Medieval Tournament")
296+
# Video playback area
297+
video_label = tk.Label(frame, text="Joust & Swordsplay Training Video", font=("Arial", 16, "bold"), fg="#ffd700", bg="#1a1a2e")
298+
video_label.pack(pady=(20, 10))
299+
video_player = tkintervideo.TkinterVideo(frame, width=640, height=360, bg="#0f3460")
300+
video_player.pack(pady=10)
301+
video_path = "assets/medieval_training.mp4"
302+
try:
303+
video_player.load(video_path)
304+
video_player.set_size((640, 360))
305+
except Exception as e:
306+
error_label = tk.Label(frame, text=f"Video not found: {video_path}", fg="#e94560", bg="#1a1a2e", font=("Arial", 12, "bold"))
307+
error_label.pack()
308+
controls = tk.Frame(frame, bg="#1a1a2e")
309+
controls.pack(pady=5)
310+
tk.Button(controls, text="Play", command=video_player.play, bg="#16c784", fg="white", font=("Arial", 11, "bold"), padx=15, pady=5).pack(side=tk.LEFT, padx=5)
311+
tk.Button(controls, text="Pause", command=video_player.pause, bg="#ffd700", fg="black", font=("Arial", 11, "bold"), padx=15, pady=5).pack(side=tk.LEFT, padx=5)
312+
# Multiplayer area
313+
mp_label = tk.Label(frame, text="Multiplayer Lobby (Server-Based)", font=("Arial", 15, "bold"), fg="#16c784", bg="#1a1a2e")
314+
mp_label.pack(pady=(30, 10))
315+
mp_frame = tk.Frame(frame, bg="#16213e")
316+
mp_frame.pack(fill=tk.X, padx=20, pady=5)
317+
self.mp_status = tk.Label(mp_frame, text="Not connected", fg="#e94560", bg="#16213e", font=("Arial", 11))
318+
self.mp_status.pack(side=tk.LEFT, padx=5)
319+
self.mp_chat = tk.Text(mp_frame, height=5, width=60, bg="#0f3460", fg="#ffd700", font=("Courier", 10), relief=tk.FLAT)
320+
self.mp_chat.pack(side=tk.LEFT, padx=5)
321+
self.mp_chat.config(state=tk.DISABLED)
322+
chat_entry = tk.Entry(mp_frame, font=("Arial", 11), width=30)
323+
chat_entry.pack(side=tk.LEFT, padx=5)
324+
def send_chat():
325+
msg = chat_entry.get().strip()
326+
if msg:
327+
asyncio.run_coroutine_threadsafe(self._mp_send(msg), self._mp_loop)
328+
chat_entry.delete(0, tk.END)
329+
tk.Button(mp_frame, text="Send", command=send_chat, bg="#16c784", fg="white", font=("Arial", 10, "bold"), padx=10, pady=2).pack(side=tk.LEFT, padx=5)
330+
tk.Button(mp_frame, text="Connect", command=lambda: threading.Thread(target=self._mp_connect, daemon=True).start(), bg="#ffd700", fg="black", font=("Arial", 10, "bold"), padx=10, pady=2).pack(side=tk.LEFT, padx=5)
331+
# Training/feedback area
332+
training_label = tk.Label(frame, text="Algorithmic Training & Feedback", font=("Arial", 15, "bold"), fg="#16c784", bg="#1a1a2e")
333+
training_label.pack(pady=(30, 10))
334+
training_text = tk.Text(frame, height=10, bg="#16213e", fg="#ffd700", font=("Courier", 11), relief=tk.FLAT)
335+
training_text.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
336+
training_text.insert(1.0, "Welcome to the Medieval Tournament!\n\n- Watch the joust & swordsplay training video.\n- Practice your moves and strategies.\n- Chat with other players in the lobby.\n- Receive feedback and tips based on your performance.\n\n[Algorithmic learning and interactive feedback will be implemented here.]")
337+
training_text.config(state=tk.DISABLED)
338+
339+
def _mp_connect(self):
340+
import asyncio
341+
import websockets
342+
self._mp_loop = asyncio.new_event_loop()
343+
asyncio.set_event_loop(self._mp_loop)
344+
self._mp_loop.run_until_complete(self._mp_client())
345+
346+
async def _mp_client(self):
347+
import websockets
348+
try:
349+
self.mp_status.config(text="Connecting...", fg="#ffd700")
350+
self._mp_ws = await websockets.connect("ws://localhost:8765")
351+
self.mp_status.config(text="Connected", fg="#16c784")
352+
async for message in self._mp_ws:
353+
self._mp_add_chat(message)
354+
except Exception as e:
355+
self.mp_status.config(text=f"Connection failed: {e}", fg="#e94560")
356+
357+
async def _mp_send(self, msg):
358+
try:
359+
await self._mp_ws.send(msg)
360+
except Exception:
361+
pass
362+
363+
def _mp_add_chat(self, msg):
364+
self.mp_chat.config(state=tk.NORMAL)
365+
self.mp_chat.insert(tk.END, msg + "\n")
366+
self.mp_chat.see(tk.END)
367+
self.mp_chat.config(state=tk.DISABLED)
194368

195369
def _create_tournament_tab(self) -> None:
196370
"""Tournament management tab"""

0 commit comments

Comments
 (0)