awacke1 commited on
Commit
6c9249b
·
verified ·
1 Parent(s): 1cfe62d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +471 -18
index.html CHANGED
@@ -1,19 +1,472 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Legends of the Triple Eclipse</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap');
10
+ body {
11
+ margin: 0;
12
+ overflow: hidden;
13
+ background-color: #0a0a10;
14
+ font-family: 'Cinzel', serif;
15
+ color: #e0e0e0;
16
+ }
17
+ canvas {
18
+ display: block;
19
+ }
20
+ #ui-container {
21
+ position: absolute;
22
+ top: 0;
23
+ left: 0;
24
+ width: 100%;
25
+ height: 100%;
26
+ pointer-events: none;
27
+ display: flex;
28
+ flex-direction: column;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ }
32
+ .ui-panel {
33
+ background-color: rgba(10, 10, 16, 0.7);
34
+ border: 1px solid rgba(128, 0, 255, 0.4);
35
+ border-radius: 12px;
36
+ padding: 1rem;
37
+ margin: 1rem;
38
+ backdrop-filter: blur(5px);
39
+ box-shadow: 0 0 20px rgba(128, 0, 255, 0.3);
40
+ pointer-events: auto;
41
+ }
42
+ .character-btn {
43
+ background-color: rgba(30, 30, 50, 0.8);
44
+ border: 1px solid transparent;
45
+ border-image-slice: 1;
46
+ padding: 0.75rem 1.5rem;
47
+ border-radius: 8px;
48
+ cursor: pointer;
49
+ transition: all 0.3s ease;
50
+ text-transform: uppercase;
51
+ letter-spacing: 1px;
52
+ font-weight: 700;
53
+ }
54
+ .character-btn:hover, .character-btn.active {
55
+ transform: translateY(-2px);
56
+ box-shadow: 0 0 15px var(--glow-color, #fff);
57
+ border: 1px solid var(--glow-color, #fff);
58
+ background-color: var(--bg-color, #303050);
59
+ }
60
+ #instructions {
61
+ max-width: 400px;
62
+ text-align: center;
63
+ }
64
+ </style>
65
+ </head>
66
+ <body>
67
+
68
+ <canvas id="gameCanvas"></canvas>
69
+
70
+ <div id="ui-container">
71
+ <!-- Top Panel: Character Selection -->
72
+ <div id="character-selection" class="ui-panel">
73
+ <h1 class="text-2xl font-bold text-center mb-4">Choose Your Legend</h1>
74
+ <div class="flex space-x-4">
75
+ <button id="sephiroth-btn" class="character-btn" style="--glow-color: #c0c0c0; --bg-color: #2d2d3a;">Sephiroth</button>
76
+ <button id="yshtola-btn" class="character-btn" style="--glow-color: #c488ff; --bg-color: #3a2d3a;">Y'shtola</button>
77
+ <button id="vivi-btn" class="character-btn" style="--glow-color: #ffb86c; --bg-color: #3a322d;">Vivi</button>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Bottom Panel: Instructions & Info -->
82
+ <div id="info-panel" class="ui-panel">
83
+ <h2 id="character-name" class="text-xl font-bold text-center">-</h2>
84
+ <p id="instructions" class="mt-2 text-sm text-gray-300">Select a character to begin. Use your mouse to shape the realm.</p>
85
+ </div>
86
+ </div>
87
+
88
+ <script type="importmap">
89
+ {
90
+ "imports": {
91
+ "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
92
+ "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
93
+ }
94
+ }
95
+ </script>
96
+
97
+ <script type="module">
98
+ import * as THREE from 'three';
99
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
100
+
101
+ // --- CORE GAME ENGINE ---
102
+ class GameEngine {
103
+ constructor() {
104
+ this.scene = new THREE.Scene();
105
+ this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
106
+ this.renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('gameCanvas'), antialias: true });
107
+ this.controls = new OrbitControls(this.camera, this.renderer.domElement);
108
+ this.raycaster = new THREE.Raycaster();
109
+ this.mouse = new THREE.Vector2();
110
+
111
+ this.activeCharacter = null;
112
+ this.terrain = null;
113
+ this.activeEffects = new THREE.Group();
114
+ this.scene.add(this.activeEffects);
115
+
116
+ this.isMouseDown = false;
117
+ this.mouseDownTime = 0;
118
+
119
+ this.init();
120
+ this.bindEvents();
121
+ this.animate();
122
+ }
123
+
124
+ init() {
125
+ // Renderer setup
126
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
127
+ this.renderer.setPixelRatio(window.devicePixelRatio);
128
+ this.renderer.shadowMap.enabled = true;
129
+
130
+ // Camera setup
131
+ this.camera.position.set(0, 50, 60);
132
+ this.controls.update();
133
+
134
+ // Lighting
135
+ const ambientLight = new THREE.AmbientLight(0x404060, 2);
136
+ this.scene.add(ambientLight);
137
+
138
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
139
+ directionalLight.position.set(50, 50, 25);
140
+ directionalLight.castShadow = true;
141
+ this.scene.add(directionalLight);
142
+
143
+ // Scene background
144
+ this.scene.fog = new THREE.FogExp2(0x0a0a10, 0.008);
145
+ this.scene.background = new THREE.Color(0x0a0a10);
146
+
147
+ // Initial Terrain
148
+ this.createTerrain();
149
+ }
150
+
151
+ createTerrain() {
152
+ if (this.terrain) this.scene.remove(this.terrain);
153
+ const geometry = new THREE.PlaneGeometry(200, 200, 100, 100);
154
+ const material = new THREE.MeshStandardMaterial({
155
+ color: 0x222233,
156
+ wireframe: false,
157
+ roughness: 0.8,
158
+ metalness: 0.2,
159
+ });
160
+ this.terrain = new THREE.Mesh(geometry, material);
161
+ this.terrain.rotation.x = -Math.PI / 2;
162
+ this.terrain.receiveShadow = true;
163
+ this.scene.add(this.terrain);
164
+ }
165
+
166
+ bindEvents() {
167
+ window.addEventListener('resize', this.onWindowResize.bind(this));
168
+ this.renderer.domElement.addEventListener('mousedown', this.onMouseDown.bind(this));
169
+ this.renderer.domElement.addEventListener('mouseup', this.onMouseUp.bind(this));
170
+ this.renderer.domElement.addEventListener('mousemove', this.onMouseMove.bind(this));
171
+ }
172
+
173
+ onWindowResize() {
174
+ this.camera.aspect = window.innerWidth / window.innerHeight;
175
+ this.camera.updateProjectionMatrix();
176
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
177
+ }
178
+
179
+ onMouseDown(event) {
180
+ this.isMouseDown = true;
181
+ this.mouseDownTime = Date.now();
182
+ this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
183
+ this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
184
+ }
185
+
186
+ onMouseMove(event) {
187
+ if(this.isMouseDown) { // Allow dragging spells
188
+ this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
189
+ this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
190
+ }
191
+ }
192
+
193
+ onMouseUp(event) {
194
+ if (!this.activeCharacter) return;
195
+
196
+ this.isMouseDown = false;
197
+ const holdDuration = Date.now() - this.mouseDownTime;
198
+
199
+ this.raycaster.setFromCamera(this.mouse, this.camera);
200
+ const intersects = this.raycaster.intersectObject(this.terrain);
201
+
202
+ if (intersects.length > 0) {
203
+ const point = intersects[0].point;
204
+ // FIX: Use .call() to set the correct 'this' context inside the trigger function
205
+ this.activeCharacter.trigger.call(this, {
206
+ type: 'cast',
207
+ position: point,
208
+ holdDuration: holdDuration
209
+ });
210
+ }
211
+ }
212
+
213
+ setActiveCharacter(character) {
214
+ this.activeCharacter = character;
215
+
216
+ // Clean up old effects
217
+ while(this.activeEffects.children.length > 0){
218
+ this.activeEffects.remove(this.activeEffects.children[0]);
219
+ }
220
+
221
+ this.createTerrain(); // Reset terrain for the new character
222
+
223
+ // Update UI
224
+ document.getElementById('character-name').textContent = character.name;
225
+ document.getElementById('instructions').textContent = character.instructions;
226
+
227
+ document.querySelectorAll('.character-btn').forEach(btn => btn.classList.remove('active'));
228
+ document.getElementById(`${character.id}-btn`).classList.add('active');
229
+
230
+ // Set scene tint
231
+ this.scene.background = new THREE.Color(character.env.bgColor);
232
+ this.scene.fog.color.set(character.env.fogColor);
233
+ }
234
+
235
+ animate() {
236
+ requestAnimationFrame(this.animate.bind(this));
237
+
238
+ // Update active effects (particles, etc.)
239
+ this.activeEffects.children.forEach(effect => {
240
+ if(effect.userData.update) {
241
+ effect.userData.update();
242
+ }
243
+ });
244
+
245
+ this.controls.update();
246
+ this.renderer.render(this.scene, this.camera);
247
+ }
248
+
249
+ deformTerrain(position, strength, radius) {
250
+ const vertices = this.terrain.geometry.attributes.position;
251
+ const localPos = this.terrain.worldToLocal(position.clone());
252
+
253
+ for (let i = 0; i < vertices.count; i++) {
254
+ const v = new THREE.Vector3().fromBufferAttribute(vertices, i);
255
+ const dist = v.distanceTo(localPos);
256
+
257
+ if (dist < radius) {
258
+ const factor = (radius - dist) / radius;
259
+ const displacement = strength * Math.cos(factor * Math.PI / 2);
260
+ vertices.setZ(i, vertices.getZ(i) + displacement);
261
+ }
262
+ }
263
+ vertices.needsUpdate = true;
264
+ }
265
+ }
266
+
267
+ // --- L-SYSTEM ENGINE ---
268
+ class LSystem {
269
+ constructor({ axiom, rules, angle }) {
270
+ this.axiom = axiom;
271
+ this.rules = rules;
272
+ this.angle = angle * (Math.PI / 180); // Convert angle to radians
273
+ this.sentence = axiom;
274
+ }
275
+
276
+ generate(iterations) {
277
+ this.sentence = this.axiom;
278
+ for (let i = 0; i < iterations; i++) {
279
+ let nextSentence = '';
280
+ for (const char of this.sentence) {
281
+ nextSentence += this.rules[char] || char;
282
+ }
283
+ this.sentence = nextSentence;
284
+ }
285
+ return this.sentence;
286
+ }
287
+ }
288
+
289
+ // --- CHARACTER DEFINITIONS ---
290
+ const CHARACTERS = {
291
+ sephiroth: {
292
+ id: 'sephiroth',
293
+ name: 'Sephiroth, Fabled Soldier',
294
+ instructions: 'Click and drag to grow dark fractal wings across the land.',
295
+ env: { bgColor: 0x101018, fogColor: 0x101018 },
296
+ lSystem: new LSystem({
297
+ axiom: 'F',
298
+ rules: { 'F': 'F+F-F-F+F' },
299
+ angle: 90
300
+ }),
301
+ trigger: function(action) { // `this` is now the game engine instance
302
+ const iterations = Math.min(4, Math.floor(action.holdDuration / 400) + 1);
303
+ const sentence = this.activeCharacter.lSystem.generate(iterations);
304
+ this.activeCharacter.visualize(sentence, action.position, this);
305
+ this.deformTerrain(action.position, -1, 30);
306
+ },
307
+ visualize: function(sentence, startPos, engine) {
308
+ const material = new THREE.LineBasicMaterial({ color: 0xCCCCCC, transparent: true, opacity: 0.8 });
309
+ const points = [];
310
+ const state = {
311
+ pos: startPos.clone(),
312
+ dir: new THREE.Vector3(1, 0, 0),
313
+ len: 5 - (sentence.length / 500),
314
+ };
315
+
316
+ for (const char of sentence) {
317
+ if (char === 'F') {
318
+ const newPos = state.pos.clone().addScaledVector(state.dir, state.len);
319
+ points.push(state.pos.clone(), newPos.clone());
320
+ state.pos = newPos;
321
+ } else if (char === '+') {
322
+ state.dir.applyAxisAngle(new THREE.Vector3(0, 1, 0), this.lSystem.angle);
323
+ } else if (char === '-') {
324
+ state.dir.applyAxisAngle(new THREE.Vector3(0, 1, 0), -this.lSystem.angle);
325
+ }
326
+ }
327
+ const geometry = new THREE.BufferGeometry().setFromPoints(points);
328
+ const line = new THREE.LineSegments(geometry, material);
329
+ line.userData.life = 500; // frames
330
+ line.userData.update = () => {
331
+ line.material.opacity -= 0.002;
332
+ line.userData.life--;
333
+ if (line.userData.life <= 0) engine.activeEffects.remove(line);
334
+ };
335
+ engine.activeEffects.add(line);
336
+ }
337
+ },
338
+ yshtola: {
339
+ id: 'yshtola',
340
+ name: 'Y\'shtola, Night\'s Blessed',
341
+ instructions: 'Click to grow a spiraling floral ward.',
342
+ env: { bgColor: 0x181018, fogColor: 0x181018 },
343
+ lSystem: new LSystem({
344
+ axiom: 'X',
345
+ rules: { 'X': 'F-[[X]+X]+F[+FX]-X', 'F': 'FF' },
346
+ angle: 25
347
+ }),
348
+ trigger: function(action) { // `this` is now the game engine instance
349
+ const iterations = Math.min(4, Math.floor(action.holdDuration / 300) + 1);
350
+ const sentence = this.activeCharacter.lSystem.generate(iterations);
351
+ this.activeCharacter.visualize(sentence, action.position, this);
352
+ this.deformTerrain(action.position, 0.5, 20);
353
+ },
354
+ visualize: function(sentence, startPos, engine) {
355
+ const material = new THREE.PointsMaterial({ color: 0xc488ff, size: 0.5, transparent: true, blending: THREE.AdditiveBlending });
356
+ const points = [];
357
+ const stack = [];
358
+ const state = {
359
+ pos: startPos.clone(),
360
+ dir: new THREE.Vector3(0, 0, 1),
361
+ len: 2,
362
+ };
363
+
364
+ for (const char of sentence) {
365
+ switch (char) {
366
+ case 'F':
367
+ state.pos.addScaledVector(state.dir, state.len);
368
+ const newPoint = state.pos.clone();
369
+ newPoint.y += Math.sin(points.length * 0.1) * 2; // Add some wavy variance
370
+ points.push(newPoint);
371
+ break;
372
+ case '+':
373
+ state.dir.applyAxisAngle(new THREE.Vector3(0, 1, 0), this.lSystem.angle);
374
+ break;
375
+ case '-':
376
+ state.dir.applyAxisAngle(new THREE.Vector3(0, 1, 0), -this.lSystem.angle);
377
+ break;
378
+ case '[':
379
+ stack.push({ pos: state.pos.clone(), dir: state.dir.clone() });
380
+ break;
381
+ case ']':
382
+ const popped = stack.pop();
383
+ state.pos = popped.pos;
384
+ state.dir = popped.dir;
385
+ break;
386
+ }
387
+ }
388
+
389
+ const geometry = new THREE.BufferGeometry().setFromPoints(points);
390
+ const particles = new THREE.Points(geometry, material);
391
+ particles.userData.life = 600;
392
+ particles.userData.update = () => {
393
+ particles.material.opacity -= 0.0015;
394
+ particles.material.size -= 0.001;
395
+ particles.userData.life--;
396
+ if (particles.userData.life <= 0 || particles.material.size <=0) engine.activeEffects.remove(particles);
397
+ };
398
+ engine.activeEffects.add(particles);
399
+ }
400
+ },
401
+ vivi: {
402
+ id: 'vivi',
403
+ name: 'Vivi Ornitier, Fire Incarnate',
404
+ instructions: 'Hold and release to unleash a recursive fire blast.',
405
+ env: { bgColor: 0x181410, fogColor: 0x181410 },
406
+ lSystem: new LSystem({ // This L-System is for pattern, not structure
407
+ axiom: 'A',
408
+ rules: { 'A': 'AB', 'B': 'A' },
409
+ angle: 0 // Not used for this character
410
+ }),
411
+ trigger: function(action) { // `this` is now the game engine instance
412
+ const iterations = Math.min(8, Math.floor(action.holdDuration / 200) + 1);
413
+ const sentence = this.activeCharacter.lSystem.generate(iterations);
414
+ this.activeCharacter.visualize(sentence, action.position, this);
415
+ this.deformTerrain(action.position, -0.5, 10 + iterations * 2);
416
+ },
417
+ visualize: function(sentence, centerPos, engine) {
418
+ const count = sentence.length;
419
+ const material = new THREE.PointsMaterial({
420
+ size: 2,
421
+ color: 0xffb86c,
422
+ blending: THREE.AdditiveBlending,
423
+ transparent: true,
424
+ depthWrite: false,
425
+ });
426
+
427
+ for(let i = 0; i < count; i++) {
428
+ const geometry = new THREE.BufferGeometry();
429
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0], 3));
430
+ const particle = new THREE.Points(geometry, material.clone());
431
+
432
+ particle.position.copy(centerPos);
433
+
434
+ const phi = Math.random() * Math.PI * 2;
435
+ const theta = Math.acos((Math.random() * 2) - 1);
436
+ const radius = (i / count) * (count / 10 + 5) * 1.5;
437
+
438
+ particle.userData.velocity = new THREE.Vector3(
439
+ radius * Math.sin(theta) * Math.cos(phi),
440
+ radius * Math.sin(theta) * Math.sin(phi),
441
+ radius * Math.cos(theta)
442
+ ).multiplyScalar(0.01 + Math.random() * 0.02);
443
+
444
+ particle.userData.life = 100 + Math.random() * 100;
445
+
446
+ particle.userData.update = () => {
447
+ particle.position.add(particle.userData.velocity);
448
+ particle.userData.life--;
449
+ particle.material.opacity = (particle.userData.life / 150);
450
+ if (particle.userData.life <= 0) {
451
+ engine.activeEffects.remove(particle);
452
+ }
453
+ };
454
+ engine.activeEffects.add(particle);
455
+ }
456
+ }
457
+ }
458
+ };
459
+
460
+ // --- APPLICATION START ---
461
+ document.addEventListener('DOMContentLoaded', () => {
462
+ const game = new GameEngine();
463
+
464
+ // Bind UI buttons
465
+ document.getElementById('sephiroth-btn').addEventListener('click', () => game.setActiveCharacter(CHARACTERS.sephiroth));
466
+ document.getElementById('yshtola-btn').addEventListener('click', () => game.setActiveCharacter(CHARACTERS.yshtola));
467
+ document.getElementById('vivi-btn').addEventListener('click', () => game.setActiveCharacter(CHARACTERS.vivi));
468
+ });
469
+
470
+ </script>
471
+ </body>
472
  </html>