awacke1 commited on
Commit
ce1e7f3
·
verified ·
1 Parent(s): 22bfb1c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +262 -0
app.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import numpy as np
3
+ import pandas as pd
4
+ import json
5
+ import asyncio
6
+ import websockets
7
+ import base64
8
+ from io import BytesIO
9
+ from PIL import Image
10
+ import pythreejs as three
11
+ from streamlit.components.v1 import html
12
+
13
+ # --- Shared World State ---
14
+ @st.cache_resource
15
+ def init_world_state():
16
+ return {
17
+ "players": {}, # {player_id: {x, y, z}}
18
+ "elements": [], # List of placed comic elements {id, type, x, y, content}
19
+ "bounds": 100, # Radius of the spherical world
20
+ }
21
+
22
+ world_state = init_world_state()
23
+
24
+ # --- Multiplayer WebSocket Server ---
25
+ async def websocket_handler(websocket, path):
26
+ player_id = str(id(websocket))
27
+ world_state["players"][player_id] = {"x": 0, "y": 0, "z": 0}
28
+
29
+ try:
30
+ async for message in websocket:
31
+ data = json.loads(message)
32
+ if "position" in data:
33
+ world_state["players"][player_id] = data["position"]
34
+ elif "element" in data:
35
+ world_state["elements"].append(data["element"])
36
+ await websocket.send(json.dumps(world_state))
37
+ finally:
38
+ del world_state["players"][player_id]
39
+
40
+ # Start WebSocket server in a separate thread
41
+ async def start_websocket_server():
42
+ server = await websockets.serve(websocket_handler, "localhost", 8765)
43
+ await asyncio.Future() # Run forever
44
+
45
+ if "websocket_server" not in st.session_state:
46
+ st.session_state.websocket_server = True
47
+ asyncio.run_coroutine_threadsafe(start_websocket_server(), asyncio.get_event_loop())
48
+
49
+ # --- Streamlit App ---
50
+ st.title("Neon Emissary’s Promise: A Sci-Fi Horror Comic Adventure")
51
+
52
+ # --- Comic Cover Display with Improved Font Styles ---
53
+ st.header("Comic Cover: Neon Emissary’s Promise")
54
+ st.markdown("""
55
+ A stained glass mosaic artwork of a futuristic envoy, composed of jewel-toned, colored glass fragments arranged into intricate patterns, with refracted light and vibrant, luminous reflections, standing beneath a vault of radiant pink chrome.
56
+ """)
57
+
58
+ # Custom CSS for improved font styles inspired by horror comic aesthetics
59
+ st.markdown("""
60
+ <style>
61
+ .comic-title {
62
+ font-family: 'Creepster', cursive; /* A jagged, horror-inspired font */
63
+ font-size: 48px;
64
+ color: #ff0000; /* Blood red */
65
+ text-shadow: 3px 3px 5px #000, -3px -3px 5px #ff00ff; /* Neon pink shadow for depth */
66
+ letter-spacing: 2px;
67
+ transform: rotate(-5deg); /* Slight tilt for dynamic tension */
68
+ margin-bottom: 10px;
69
+ }
70
+ .comic-subtitle {
71
+ font-family: 'Nosifer', cursive; /* Distressed, dripping font */
72
+ font-size: 24px;
73
+ color: #00ff00; /* Neon green */
74
+ text-shadow: 2px 2px 4px #000;
75
+ letter-spacing: 1px;
76
+ transform: rotate(3deg);
77
+ }
78
+ </style>
79
+ """, unsafe_allow_html=True)
80
+
81
+ st.markdown('<div class="comic-title">Neon Emissary’s Promise</div>', unsafe_allow_html=True)
82
+ st.markdown('<div class="comic-subtitle">A Tale of Love and Rebirth in Know It All</div>', unsafe_allow_html=True)
83
+
84
+ # --- Game Interface ---
85
+ st.header("Interactive Comic Adventure")
86
+
87
+ # Player ID for multiplayer
88
+ if "player_id" not in st.session_state:
89
+ st.session_state.player_id = str(np.random.randint(1000000))
90
+
91
+ # --- Drag-and-Drop Canvas with JavaScript ---
92
+ st.subheader("Comic Canvas (Drag and Drop Elements)")
93
+ canvas_html = """
94
+ <canvas id="comicCanvas" width="800" height="400" style="border:1px solid black;"></canvas>
95
+ <div>
96
+ <button onclick="addElement('character')">Add Character</button>
97
+ <button onclick="addElement('speech')">Add Speech Bubble</button>
98
+ </div>
99
+ <script>
100
+ const canvas = document.getElementById('comicCanvas');
101
+ const ctx = canvas.getContext('2d');
102
+ let elements = [];
103
+ let selectedElement = null;
104
+
105
+ function drawCanvas() {
106
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
107
+ elements.forEach(el => {
108
+ if (el.type === 'character') {
109
+ ctx.fillStyle = '#ff00ff';
110
+ ctx.fillRect(el.x, el.y, 50, 50);
111
+ } else if (el.type === 'speech') {
112
+ ctx.fillStyle = '#ffffff';
113
+ ctx.fillRect(el.x, el.y, 100, 40);
114
+ ctx.fillStyle = '#000000';
115
+ ctx.font = '12px Arial';
116
+ ctx.fillText(el.content || 'Speak...', el.x + 10, el.y + 25);
117
+ }
118
+ });
119
+ }
120
+
121
+ function addElement(type) {
122
+ const newElement = { id: Date.now(), type, x: 50, y: 50, content: type === 'speech' ? 'Speak...' : null };
123
+ elements.push(newElement);
124
+ drawCanvas();
125
+ // Send to WebSocket
126
+ const ws = new WebSocket('ws://localhost:8765');
127
+ ws.onopen = () => ws.send(JSON.stringify({ element: newElement }));
128
+ }
129
+
130
+ canvas.addEventListener('mousedown', (e) => {
131
+ const rect = canvas.getBoundingClientRect();
132
+ const x = e.clientX - rect.left;
133
+ const y = e.clientY - rect.top;
134
+ selectedElement = elements.find(el =>
135
+ x >= el.x && x <= el.x + (el.type === 'speech' ? 100 : 50) &&
136
+ y >= el.y && y <= el.y + (el.type === 'speech' ? 40 : 50)
137
+ );
138
+ });
139
+
140
+ canvas.addEventListener('mousemove', (e) => {
141
+ if (selectedElement) {
142
+ const rect = canvas.getBoundingClientRect();
143
+ selectedElement.x = e.clientX - rect.left - (selectedElement.type === 'speech' ? 50 : 25);
144
+ selectedElement.y = e.clientY - rect.top - (selectedElement.type === 'speech' ? 20 : 25);
145
+ drawCanvas();
146
+ }
147
+ });
148
+
149
+ canvas.addEventListener('mouseup', () => {
150
+ selectedElement = null;
151
+ });
152
+
153
+ // WebSocket to receive updates
154
+ const ws = new WebSocket('ws://localhost:8765');
155
+ ws.onmessage = (event) => {
156
+ const data = JSON.parse(event.data);
157
+ elements = data.elements;
158
+ drawCanvas();
159
+ };
160
+ </script>
161
+ """
162
+ html(canvas_html, height=500)
163
+
164
+ # --- 3D Particle System for Multiplayer ---
165
+ st.subheader("Multiplayer World (3D Particle System)")
166
+ threejs_html = """
167
+ <script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
168
+ <div id="threejs-container" style="width:800px; height:400px;"></div>
169
+ <script>
170
+ const scene = new THREE.Scene();
171
+ const camera = new THREE.PerspectiveCamera(75, 800 / 400, 0.1, 1000);
172
+ const renderer = new THREE.WebGLRenderer();
173
+ renderer.setSize(800, 400);
174
+ document.getElementById('threejs-container').appendChild(renderer.domElement);
175
+
176
+ // Create a sphere of particles
177
+ const particles = new THREE.Group();
178
+ const particleCount = 100;
179
+ const radius = 100;
180
+ for (let i = 0; i < particleCount; i++) {
181
+ const geometry = new THREE.SphereGeometry(1, 32, 32);
182
+ const material = new THREE.MeshBasicMaterial({ color: 0xff00ff });
183
+ const particle = new THREE.Mesh(geometry, material);
184
+ const theta = Math.random() * Math.PI * 2;
185
+ const phi = Math.acos(2 * Math.random() - 1);
186
+ particle.position.set(
187
+ radius * Math.sin(phi) * Math.cos(theta),
188
+ radius * Math.sin(phi) * Math.sin(theta),
189
+ radius * Math.cos(phi)
190
+ );
191
+ particles.add(particle);
192
+ }
193
+ scene.add(particles);
194
+
195
+ // Players as glowing spheres
196
+ const playersGroup = new THREE.Group();
197
+ scene.add(playersGroup);
198
+
199
+ camera.position.z = 150;
200
+
201
+ function animate() {
202
+ requestAnimationFrame(animate);
203
+ particles.rotation.y += 0.01;
204
+ renderer.render(scene, camera);
205
+ }
206
+ animate();
207
+
208
+ // WebSocket to update player positions
209
+ const ws = new WebSocket('ws://localhost:8765');
210
+ ws.onmessage = (event) => {
211
+ const data = JSON.parse(event.data);
212
+ playersGroup.children.forEach(child => playersGroup.remove(child));
213
+ Object.entries(data.players).forEach(([id, pos]) => {
214
+ const geometry = new THREE.SphereGeometry(3, 32, 32);
215
+ const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
216
+ const player = new THREE.Mesh(geometry, material);
217
+ player.position.set(pos.x, pos.y, pos.z);
218
+ playersGroup.add(player);
219
+ });
220
+ };
221
+
222
+ // Send player position
223
+ document.addEventListener('mousemove', (e) => {
224
+ const x = (e.clientX / window.innerWidth) * 200 - 100;
225
+ const y = -(e.clientY / window.innerHeight) * 200 + 100;
226
+ const z = 0;
227
+ if (ws.readyState === WebSocket.OPEN) {
228
+ ws.send(JSON.stringify({ position: { x, y, z } }));
229
+ }
230
+ });
231
+ </script>
232
+ """
233
+ html(threejs_html, height=450)
234
+
235
+ # --- Story Dialogue ---
236
+ st.header("Story: Neon Emissary’s Promise")
237
+ st.write("""
238
+ **Scene:** A futuristic envoy, rendered as a stained glass mosaic, stands beneath a vault of radiant pink chrome in the city of Know It All, where giant brain skyscrapers pulse with LED lights.
239
+
240
+ **Dialogue:**
241
+
242
+ *“In these kaleidoscopic shards, every broken piece whispers the promise of rebirth,”* she confesses, eyes sparkling amid refracted light.
243
+
244
+ *“Our love etches its vow into every fragment of this luminous city,”* he replies, as vibrant reflections underscore their fateful meeting.
245
+ """)
246
+
247
+ # --- World Building Tests ---
248
+ st.header("World Building Tests")
249
+ if st.button("Generate Random Event"):
250
+ event = np.random.choice([
251
+ "A neon storm erupts, casting fractured light across the city.",
252
+ "A brain skyscraper pulses, revealing a hidden memory in the shards.",
253
+ "A rival envoy appears, challenging the promise of rebirth."
254
+ ])
255
+ st.write(event)
256
+ world_state["elements"].append({
257
+ "id": str(np.random.randint(1000000)),
258
+ "type": "speech",
259
+ "x": np.random.randint(50, 750),
260
+ "y": np.random.randint(50, 350),
261
+ "content": event
262
+ })