| import streamlit as st |
| import numpy as np |
| import pandas as pd |
| import json |
| import asyncio |
| import websockets |
| import base64 |
| from io import BytesIO |
| from PIL import Image |
| import pythreejs as three |
| from streamlit.components.v1 import html |
|
|
| |
| @st.cache_resource |
| def init_world_state(): |
| return { |
| "players": {}, |
| "elements": [], |
| "bounds": 100, |
| } |
|
|
| world_state = init_world_state() |
|
|
| |
| async def websocket_handler(websocket, path): |
| player_id = str(id(websocket)) |
| world_state["players"][player_id] = {"x": 0, "y": 0, "z": 0} |
| |
| try: |
| async for message in websocket: |
| data = json.loads(message) |
| if "position" in data: |
| world_state["players"][player_id] = data["position"] |
| elif "element" in data: |
| world_state["elements"].append(data["element"]) |
| await websocket.send(json.dumps(world_state)) |
| finally: |
| del world_state["players"][player_id] |
|
|
| |
| async def start_websocket_server(): |
| server = await websockets.serve(websocket_handler, "localhost", 8765) |
| await asyncio.Future() |
|
|
| if "websocket_server" not in st.session_state: |
| st.session_state.websocket_server = True |
| asyncio.run_coroutine_threadsafe(start_websocket_server(), asyncio.get_event_loop()) |
|
|
| |
| st.title("Neon Emissary’s Promise: A Sci-Fi Horror Comic Adventure") |
|
|
| |
| st.header("Comic Cover: Neon Emissary’s Promise") |
| st.markdown(""" |
| 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. |
| """) |
|
|
| |
| st.markdown(""" |
| <style> |
| .comic-title { |
| font-family: 'Creepster', cursive; /* A jagged, horror-inspired font */ |
| font-size: 48px; |
| color: #ff0000; /* Blood red */ |
| text-shadow: 3px 3px 5px #000, -3px -3px 5px #ff00ff; /* Neon pink shadow for depth */ |
| letter-spacing: 2px; |
| transform: rotate(-5deg); /* Slight tilt for dynamic tension */ |
| margin-bottom: 10px; |
| } |
| .comic-subtitle { |
| font-family: 'Nosifer', cursive; /* Distressed, dripping font */ |
| font-size: 24px; |
| color: #00ff00; /* Neon green */ |
| text-shadow: 2px 2px 4px #000; |
| letter-spacing: 1px; |
| transform: rotate(3deg); |
| } |
| </style> |
| """, unsafe_allow_html=True) |
|
|
| st.markdown('<div class="comic-title">Neon Emissary’s Promise</div>', unsafe_allow_html=True) |
| st.markdown('<div class="comic-subtitle">A Tale of Love and Rebirth in Know It All</div>', unsafe_allow_html=True) |
|
|
| |
| st.header("Interactive Comic Adventure") |
|
|
| |
| if "player_id" not in st.session_state: |
| st.session_state.player_id = str(np.random.randint(1000000)) |
|
|
| |
| st.subheader("Comic Canvas (Drag and Drop Elements)") |
| canvas_html = """ |
| <canvas id="comicCanvas" width="800" height="400" style="border:1px solid black;"></canvas> |
| <div> |
| <button onclick="addElement('character')">Add Character</button> |
| <button onclick="addElement('speech')">Add Speech Bubble</button> |
| </div> |
| <script> |
| const canvas = document.getElementById('comicCanvas'); |
| const ctx = canvas.getContext('2d'); |
| let elements = []; |
| let selectedElement = null; |
| |
| function drawCanvas() { |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| elements.forEach(el => { |
| if (el.type === 'character') { |
| ctx.fillStyle = '#ff00ff'; |
| ctx.fillRect(el.x, el.y, 50, 50); |
| } else if (el.type === 'speech') { |
| ctx.fillStyle = '#ffffff'; |
| ctx.fillRect(el.x, el.y, 100, 40); |
| ctx.fillStyle = '#000000'; |
| ctx.font = '12px Arial'; |
| ctx.fillText(el.content || 'Speak...', el.x + 10, el.y + 25); |
| } |
| }); |
| } |
| |
| function addElement(type) { |
| const newElement = { id: Date.now(), type, x: 50, y: 50, content: type === 'speech' ? 'Speak...' : null }; |
| elements.push(newElement); |
| drawCanvas(); |
| // Send to WebSocket |
| const ws = new WebSocket('ws://localhost:8765'); |
| ws.onopen = () => ws.send(JSON.stringify({ element: newElement })); |
| } |
| |
| canvas.addEventListener('mousedown', (e) => { |
| const rect = canvas.getBoundingClientRect(); |
| const x = e.clientX - rect.left; |
| const y = e.clientY - rect.top; |
| selectedElement = elements.find(el => |
| x >= el.x && x <= el.x + (el.type === 'speech' ? 100 : 50) && |
| y >= el.y && y <= el.y + (el.type === 'speech' ? 40 : 50) |
| ); |
| }); |
| |
| canvas.addEventListener('mousemove', (e) => { |
| if (selectedElement) { |
| const rect = canvas.getBoundingClientRect(); |
| selectedElement.x = e.clientX - rect.left - (selectedElement.type === 'speech' ? 50 : 25); |
| selectedElement.y = e.clientY - rect.top - (selectedElement.type === 'speech' ? 20 : 25); |
| drawCanvas(); |
| } |
| }); |
| |
| canvas.addEventListener('mouseup', () => { |
| selectedElement = null; |
| }); |
| |
| // WebSocket to receive updates |
| const ws = new WebSocket('ws://localhost:8765'); |
| ws.onmessage = (event) => { |
| const data = JSON.parse(event.data); |
| elements = data.elements; |
| drawCanvas(); |
| }; |
| </script> |
| """ |
| html(canvas_html, height=500) |
|
|
| |
| st.subheader("Multiplayer World (3D Particle System)") |
| threejs_html = """ |
| <script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script> |
| <div id="threejs-container" style="width:800px; height:400px;"></div> |
| <script> |
| const scene = new THREE.Scene(); |
| const camera = new THREE.PerspectiveCamera(75, 800 / 400, 0.1, 1000); |
| const renderer = new THREE.WebGLRenderer(); |
| renderer.setSize(800, 400); |
| document.getElementById('threejs-container').appendChild(renderer.domElement); |
| |
| // Create a sphere of particles |
| const particles = new THREE.Group(); |
| const particleCount = 100; |
| const radius = 100; |
| for (let i = 0; i < particleCount; i++) { |
| const geometry = new THREE.SphereGeometry(1, 32, 32); |
| const material = new THREE.MeshBasicMaterial({ color: 0xff00ff }); |
| const particle = new THREE.Mesh(geometry, material); |
| const theta = Math.random() * Math.PI * 2; |
| const phi = Math.acos(2 * Math.random() - 1); |
| particle.position.set( |
| radius * Math.sin(phi) * Math.cos(theta), |
| radius * Math.sin(phi) * Math.sin(theta), |
| radius * Math.cos(phi) |
| ); |
| particles.add(particle); |
| } |
| scene.add(particles); |
| |
| // Players as glowing spheres |
| const playersGroup = new THREE.Group(); |
| scene.add(playersGroup); |
| |
| camera.position.z = 150; |
| |
| function animate() { |
| requestAnimationFrame(animate); |
| particles.rotation.y += 0.01; |
| renderer.render(scene, camera); |
| } |
| animate(); |
| |
| // WebSocket to update player positions |
| const ws = new WebSocket('ws://localhost:8765'); |
| ws.onmessage = (event) => { |
| const data = JSON.parse(event.data); |
| playersGroup.children.forEach(child => playersGroup.remove(child)); |
| Object.entries(data.players).forEach(([id, pos]) => { |
| const geometry = new THREE.SphereGeometry(3, 32, 32); |
| const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); |
| const player = new THREE.Mesh(geometry, material); |
| player.position.set(pos.x, pos.y, pos.z); |
| playersGroup.add(player); |
| }); |
| }; |
| |
| // Send player position |
| document.addEventListener('mousemove', (e) => { |
| const x = (e.clientX / window.innerWidth) * 200 - 100; |
| const y = -(e.clientY / window.innerHeight) * 200 + 100; |
| const z = 0; |
| if (ws.readyState === WebSocket.OPEN) { |
| ws.send(JSON.stringify({ position: { x, y, z } })); |
| } |
| }); |
| </script> |
| """ |
| html(threejs_html, height=450) |
|
|
| |
| st.header("Story: Neon Emissary’s Promise") |
| st.write(""" |
| **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. |
| |
| **Dialogue:** |
| |
| *“In these kaleidoscopic shards, every broken piece whispers the promise of rebirth,”* she confesses, eyes sparkling amid refracted light. |
| |
| *“Our love etches its vow into every fragment of this luminous city,”* he replies, as vibrant reflections underscore their fateful meeting. |
| """) |
|
|
| |
| st.header("World Building Tests") |
| if st.button("Generate Random Event"): |
| event = np.random.choice([ |
| "A neon storm erupts, casting fractured light across the city.", |
| "A brain skyscraper pulses, revealing a hidden memory in the shards.", |
| "A rival envoy appears, challenging the promise of rebirth." |
| ]) |
| st.write(event) |
| world_state["elements"].append({ |
| "id": str(np.random.randint(1000000)), |
| "type": "speech", |
| "x": np.random.randint(50, 750), |
| "y": np.random.randint(50, 350), |
| "content": event |
| }) |