KBLLR commited on
Commit
7ae85db
·
verified ·
1 Parent(s): 683a2a8

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +758 -19
  3. prompts.txt +2 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Character Selector
3
- emoji: 😻
4
- colorFrom: indigo
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: character-selector
3
+ emoji: 🐳
4
+ colorFrom: yellow
5
+ colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,758 @@
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>3D Character World</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
11
+ <style>
12
+ body {
13
+ margin: 0;
14
+ overflow: hidden;
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ }
17
+ #canvas {
18
+ display: block;
19
+ }
20
+ .transition-all {
21
+ transition: all 0.3s ease;
22
+ }
23
+ .character-card:hover {
24
+ transform: translateY(-5px);
25
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
26
+ }
27
+ .dialog-box {
28
+ background: rgba(0, 0, 0, 0.8);
29
+ backdrop-filter: blur(5px);
30
+ }
31
+ .character-preview {
32
+ width: 100%;
33
+ height: 200px;
34
+ background: rgba(255, 255, 255, 0.1);
35
+ border-radius: 0.5rem;
36
+ }
37
+ </style>
38
+ </head>
39
+ <body class="bg-gray-900 text-white">
40
+ <!-- Welcome Screen -->
41
+ <div id="welcome-screen" class="fixed inset-0 flex items-center justify-center bg-gray-900 z-50 transition-all duration-500">
42
+ <div class="text-center max-w-2xl p-8 bg-gray-800 rounded-xl shadow-2xl">
43
+ <h1 class="text-5xl font-bold mb-6 bg-gradient-to-r from-purple-500 to-blue-500 bg-clip-text text-transparent">3D Character World</h1>
44
+ <p class="text-xl mb-8 text-gray-300">Explore a vibrant 3D world with interactive characters. Select your avatar and meet others along your journey!</p>
45
+ <button id="start-btn" class="px-8 py-3 bg-gradient-to-r from-purple-600 to-blue-600 rounded-full text-white font-bold text-lg hover:from-purple-700 hover:to-blue-700 transition-all transform hover:scale-105 shadow-lg">
46
+ Begin Adventure
47
+ </button>
48
+ </div>
49
+ </div>
50
+
51
+ <!-- Character Selection Screen -->
52
+ <div id="character-selection" class="fixed inset-0 flex items-center justify-center bg-gray-900 z-40 transition-all duration-500 opacity-0 pointer-events-none">
53
+ <div class="w-full max-w-6xl p-6">
54
+ <h2 class="text-3xl font-bold mb-8 text-center">Choose Your Character</h2>
55
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
56
+ <!-- Character cards will be dynamically inserted here -->
57
+ </div>
58
+ <div class="text-center">
59
+ <button id="confirm-character" class="px-8 py-3 bg-green-600 rounded-full text-white font-bold text-lg hover:bg-green-700 transition-all transform hover:scale-105 shadow-lg opacity-0">
60
+ Confirm Selection
61
+ </button>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <!-- World Selection Screen -->
67
+ <div id="world-selection" class="fixed inset-0 flex items-center justify-center bg-gray-900 z-30 transition-all duration-500 opacity-0 pointer-events-none">
68
+ <div class="w-full max-w-6xl p-6">
69
+ <h2 class="text-3xl font-bold mb-8 text-center">Select 5 Characters for Your World</h2>
70
+ <div class="grid grid-cols-1 md:grid-cols-5 gap-4 mb-8">
71
+ <!-- World character cards will be dynamically inserted here -->
72
+ </div>
73
+ <div class="text-center">
74
+ <button id="start-world" class="px-8 py-3 bg-green-600 rounded-full text-white font-bold text-lg hover:bg-green-700 transition-all transform hover:scale-105 shadow-lg opacity-0">
75
+ Generate World
76
+ </button>
77
+ </div>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Game UI -->
82
+ <div id="game-ui" class="fixed inset-0 pointer-events-none z-20 opacity-0 transition-all">
83
+ <div class="absolute bottom-4 left-4 bg-gray-800 bg-opacity-70 p-4 rounded-lg">
84
+ <div class="flex items-center space-x-2">
85
+ <div class="w-3 h-3 rounded-full bg-green-500"></div>
86
+ <span>WASD: Move</span>
87
+ </div>
88
+ <div class="flex items-center space-x-2">
89
+ <div class="w-3 h-3 rounded-full bg-blue-500"></div>
90
+ <span>Space: Jump</span>
91
+ </div>
92
+ <div class="flex items-center space-x-2">
93
+ <div class="w-3 h-3 rounded-full bg-purple-500"></div>
94
+ <span>Shift: Run</span>
95
+ </div>
96
+ <div class="flex items-center space-x-2">
97
+ <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
98
+ <span>Near NPC: Talk (Space)</span>
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ <!-- Dialog Box -->
104
+ <div id="dialog-box" class="fixed bottom-0 left-0 right-0 bg-gray-900 bg-opacity-90 p-6 rounded-t-2xl transform translate-y-full transition-all duration-300 z-50 max-w-4xl mx-auto">
105
+ <div class="flex items-start space-x-4">
106
+ <div id="dialog-character" class="w-16 h-16 rounded-full bg-gray-700 flex-shrink-0"></div>
107
+ <div class="flex-1">
108
+ <h3 id="dialog-name" class="text-xl font-bold mb-2">Character Name</h3>
109
+ <p id="dialog-text" class="text-gray-300">Hello there! This is a sample dialog text that will be replaced with actual dialog content.</p>
110
+ </div>
111
+ </div>
112
+ <button id="close-dialog" class="absolute top-4 right-4 text-gray-400 hover:text-white">
113
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
114
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
115
+ </svg>
116
+ </button>
117
+ </div>
118
+
119
+ <!-- Canvas for Three.js -->
120
+ <canvas id="canvas"></canvas>
121
+
122
+ <script>
123
+ // Game state
124
+ const gameState = {
125
+ currentScreen: 'welcome',
126
+ selectedCharacter: null,
127
+ selectedWorldCharacters: [],
128
+ characters: [],
129
+ worldCharacters: [],
130
+ player: null,
131
+ npcs: [],
132
+ nearbyNpc: null,
133
+ isRunning: false,
134
+ isJumping: false,
135
+ keys: {
136
+ w: false,
137
+ a: false,
138
+ s: false,
139
+ d: false,
140
+ shift: false,
141
+ space: false
142
+ }
143
+ };
144
+
145
+ // Sample character data (in a real app, these would be fetched from Google Cloud)
146
+ const characterData = [
147
+ { id: 1, name: "Warrior", modelUrl: "https://storage.googleapis.com/your-bucket-name/warrior.glb", color: "#EF4444", dialog: ["I fight for honor!", "The battlefield calls to me.", "Stay sharp!"] },
148
+ { id: 2, name: "Mage", modelUrl: "https://storage.googleapis.com/your-bucket-name/mage.glb", color: "#3B82F6", dialog: ["Magic flows through me.", "The arcane arts are limitless.", "Knowledge is power."] },
149
+ { id: 3, name: "Rogue", modelUrl: "https://storage.googleapis.com/your-bucket-name/rogue.glb", color: "#10B981", dialog: ["Shadows are my friends.", "Quick and quiet.", "Gold is always the answer."] },
150
+ { id: 4, name: "Archer", modelUrl: "https://storage.googleapis.com/your-bucket-name/archer.glb", color: "#F59E0B", dialog: ["My arrows never miss.", "The wind guides my shots.", "Aim true!"] },
151
+ { id: 5, name: "Cleric", modelUrl: "https://storage.googleapis.com/your-bucket-name/cleric.glb", color: "#8B5CF6", dialog: ["The light protects us.", "Healing is my calling.", "Have faith!"] },
152
+ { id: 6, name: "Bard", modelUrl: "https://storage.googleapis.com/your-bucket-name/bard.glb", color: "#EC4899", dialog: ["Let me sing you a tale!", "Music soothes the soul.", "Every story deserves a song."] },
153
+ { id: 7, name: "Monk", modelUrl: "https://storage.googleapis.com/your-bucket-name/monk.glb", color: "#6366F1", dialog: ["Inner peace is key.", "The body and mind are one.", "Discipline brings strength."] },
154
+ { id: 8, name: "Paladin", modelUrl: "https://storage.googleapis.com/your-bucket-name/paladin.glb", color: "#F97316", dialog: ["Justice will prevail!", "By my oath, I protect.", "Evil shall not pass."] }
155
+ ];
156
+
157
+ // Three.js variables
158
+ let scene, camera, renderer, controls;
159
+ let mixer, clock, loader;
160
+ let world;
161
+
162
+ // Initialize the app
163
+ document.addEventListener('DOMContentLoaded', () => {
164
+ // Set up UI event listeners
165
+ document.getElementById('start-btn').addEventListener('click', showCharacterSelection);
166
+ document.getElementById('confirm-character').addEventListener('click', showWorldSelection);
167
+ document.getElementById('start-world').addEventListener('click', startGame);
168
+ document.getElementById('close-dialog').addEventListener('click', closeDialog);
169
+
170
+ // Keyboard event listeners
171
+ window.addEventListener('keydown', handleKeyDown);
172
+ window.addEventListener('keyup', handleKeyUp);
173
+
174
+ // Populate character selection
175
+ populateCharacterSelection();
176
+ });
177
+
178
+ function showCharacterSelection() {
179
+ document.getElementById('welcome-screen').classList.add('opacity-0', 'pointer-events-none');
180
+ document.getElementById('character-selection').classList.remove('opacity-0', 'pointer-events-none');
181
+ }
182
+
183
+ function showWorldSelection() {
184
+ if (!gameState.selectedCharacter) return;
185
+
186
+ document.getElementById('character-selection').classList.add('opacity-0', 'pointer-events-none');
187
+ document.getElementById('world-selection').classList.remove('opacity-0', 'pointer-events-none');
188
+
189
+ // Filter out the selected character from world selection
190
+ const availableCharacters = characterData.filter(char => char.id !== gameState.selectedCharacter.id);
191
+ populateWorldSelection(availableCharacters);
192
+ }
193
+
194
+ function startGame() {
195
+ if (gameState.selectedWorldCharacters.length < 5) {
196
+ alert('Please select 5 characters for your world!');
197
+ return;
198
+ }
199
+
200
+ document.getElementById('world-selection').classList.add('opacity-0', 'pointer-events-none');
201
+ document.getElementById('game-ui').classList.remove('opacity-0');
202
+
203
+ // Initialize Three.js world
204
+ initThreeJS();
205
+ }
206
+
207
+ function populateCharacterSelection() {
208
+ const container = document.querySelector('#character-selection .grid');
209
+ container.innerHTML = '';
210
+
211
+ characterData.forEach(character => {
212
+ const card = document.createElement('div');
213
+ card.className = 'character-card bg-gray-800 rounded-xl p-4 cursor-pointer transition-all shadow-lg';
214
+ card.innerHTML = `
215
+ <div class="character-preview mb-4 flex items-center justify-center">
216
+ <div class="w-24 h-24 rounded-full" style="background-color: ${character.color};"></div>
217
+ </div>
218
+ <h3 class="text-xl font-bold text-center mb-2">${character.name}</h3>
219
+ <p class="text-gray-400 text-center">Click to select</p>
220
+ `;
221
+
222
+ card.addEventListener('click', () => {
223
+ // Deselect all cards
224
+ document.querySelectorAll('.character-card').forEach(c => {
225
+ c.classList.remove('ring-2', 'ring-purple-500');
226
+ });
227
+
228
+ // Select this card
229
+ card.classList.add('ring-2', 'ring-purple-500');
230
+
231
+ // Update selected character
232
+ gameState.selectedCharacter = character;
233
+
234
+ // Show confirm button
235
+ document.getElementById('confirm-character').classList.remove('opacity-0');
236
+ });
237
+
238
+ container.appendChild(card);
239
+ });
240
+ }
241
+
242
+ function populateWorldSelection(characters) {
243
+ const container = document.querySelector('#world-selection .grid');
244
+ container.innerHTML = '';
245
+
246
+ characters.forEach(character => {
247
+ const card = document.createElement('div');
248
+ card.className = `character-card bg-gray-800 rounded-xl p-4 cursor-pointer transition-all shadow-lg ${
249
+ gameState.selectedWorldCharacters.some(c => c.id === character.id) ? 'ring-2 ring-green-500' : ''
250
+ }`;
251
+ card.innerHTML = `
252
+ <div class="character-preview mb-4 flex items-center justify-center">
253
+ <div class="w-16 h-16 rounded-full" style="background-color: ${character.color};"></div>
254
+ </div>
255
+ <h3 class="text-lg font-bold text-center mb-2">${character.name}</h3>
256
+ <p class="text-gray-400 text-sm text-center">Click to ${gameState.selectedWorldCharacters.some(c => c.id === character.id) ? 'deselect' : 'select'}</p>
257
+ `;
258
+
259
+ card.addEventListener('click', () => {
260
+ // Check if character is already selected
261
+ const index = gameState.selectedWorldCharacters.findIndex(c => c.id === character.id);
262
+
263
+ if (index !== -1) {
264
+ // Deselect
265
+ gameState.selectedWorldCharacters.splice(index, 1);
266
+ card.classList.remove('ring-2', 'ring-green-500');
267
+ card.querySelector('p').textContent = 'Click to select';
268
+ } else {
269
+ // Select if we have less than 5
270
+ if (gameState.selectedWorldCharacters.length < 5) {
271
+ gameState.selectedWorldCharacters.push(character);
272
+ card.classList.add('ring-2', 'ring-green-500');
273
+ card.querySelector('p').textContent = 'Click to deselect';
274
+ }
275
+ }
276
+
277
+ // Update start world button
278
+ if (gameState.selectedWorldCharacters.length === 5) {
279
+ document.getElementById('start-world').classList.remove('opacity-0');
280
+ } else {
281
+ document.getElementById('start-world').classList.add('opacity-0');
282
+ }
283
+ });
284
+
285
+ container.appendChild(card);
286
+ });
287
+ }
288
+
289
+ function initThreeJS() {
290
+ // Set up Three.js scene
291
+ scene = new THREE.Scene();
292
+ scene.background = new THREE.Color(0x87CEEB); // Sky blue
293
+
294
+ // Camera
295
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
296
+ camera.position.set(0, 5, 10);
297
+
298
+ // Renderer
299
+ renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true });
300
+ renderer.setSize(window.innerWidth, window.innerHeight);
301
+ renderer.shadowMap.enabled = true;
302
+
303
+ // Clock for animations
304
+ clock = new THREE.Clock();
305
+
306
+ // Loader
307
+ loader = new THREE.GLTFLoader();
308
+
309
+ // Add lights
310
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
311
+ scene.add(ambientLight);
312
+
313
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
314
+ directionalLight.position.set(5, 10, 7);
315
+ directionalLight.castShadow = true;
316
+ directionalLight.shadow.mapSize.width = 2048;
317
+ directionalLight.shadow.mapSize.height = 2048;
318
+ scene.add(directionalLight);
319
+
320
+ // Create ground
321
+ const groundGeometry = new THREE.PlaneGeometry(100, 100);
322
+ const groundMaterial = new THREE.MeshStandardMaterial({
323
+ color: 0x4ade80,
324
+ roughness: 0.8,
325
+ metalness: 0.2
326
+ });
327
+ const ground = new THREE.Mesh(groundGeometry, groundMaterial);
328
+ ground.rotation.x = -Math.PI / 2;
329
+ ground.receiveShadow = true;
330
+ scene.add(ground);
331
+
332
+ // Add some environment objects
333
+ addEnvironmentObjects();
334
+
335
+ // Load player character
336
+ loadPlayerCharacter();
337
+
338
+ // Load NPC characters
339
+ loadNPCCharacters();
340
+
341
+ // Start animation loop
342
+ animate();
343
+
344
+ // Handle window resize
345
+ window.addEventListener('resize', () => {
346
+ camera.aspect = window.innerWidth / window.innerHeight;
347
+ camera.updateProjectionMatrix();
348
+ renderer.setSize(window.innerWidth, window.innerHeight);
349
+ });
350
+ }
351
+
352
+ function addEnvironmentObjects() {
353
+ // Add some trees
354
+ const treeGeometry = new THREE.ConeGeometry(1, 3, 8);
355
+ const treeMaterial = new THREE.MeshStandardMaterial({ color: 0x2e7d32 });
356
+
357
+ for (let i = 0; i < 20; i++) {
358
+ const tree = new THREE.Mesh(treeGeometry, treeMaterial);
359
+ tree.position.x = (Math.random() - 0.5) * 80;
360
+ tree.position.z = (Math.random() - 0.5) * 80;
361
+ tree.position.y = 1.5;
362
+ tree.castShadow = true;
363
+ scene.add(tree);
364
+
365
+ // Add trunk
366
+ const trunkGeometry = new THREE.CylinderGeometry(0.3, 0.3, 1);
367
+ const trunkMaterial = new THREE.MeshStandardMaterial({ color: 0x5e4035 });
368
+ const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
369
+ trunk.position.y = 0.5;
370
+ tree.add(trunk);
371
+ }
372
+
373
+ // Add some rocks
374
+ const rockGeometry = new THREE.SphereGeometry(0.5, 8, 8);
375
+ const rockMaterial = new THREE.MeshStandardMaterial({ color: 0x757575 });
376
+
377
+ for (let i = 0; i < 15; i++) {
378
+ const rock = new THREE.Mesh(rockGeometry, rockMaterial);
379
+ rock.position.x = (Math.random() - 0.5) * 80;
380
+ rock.position.z = (Math.random() - 0.5) * 80;
381
+ rock.position.y = 0.5;
382
+ rock.castShadow = true;
383
+ scene.add(rock);
384
+ }
385
+ }
386
+
387
+ function loadPlayerCharacter() {
388
+ // In a real app, we would load the GLTF model from the URL
389
+ // For this example, we'll create a simple placeholder
390
+
391
+ const group = new THREE.Group();
392
+
393
+ // Body
394
+ const bodyGeometry = new THREE.CylinderGeometry(0.5, 0.5, 1.5, 8);
395
+ const bodyMaterial = new THREE.MeshStandardMaterial({
396
+ color: new THREE.Color(gameState.selectedCharacter.color),
397
+ roughness: 0.7,
398
+ metalness: 0.1
399
+ });
400
+ const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
401
+ body.position.y = 0.75;
402
+ body.castShadow = true;
403
+ group.add(body);
404
+
405
+ // Head
406
+ const headGeometry = new THREE.SphereGeometry(0.4, 8, 8);
407
+ const headMaterial = new THREE.MeshStandardMaterial({ color: 0xf5d0b5 });
408
+ const head = new THREE.Mesh(headGeometry, headMaterial);
409
+ head.position.y = 1.6;
410
+ head.castShadow = true;
411
+ group.add(head);
412
+
413
+ // Arms
414
+ const armGeometry = new THREE.CylinderGeometry(0.15, 0.15, 0.8, 6);
415
+ const leftArm = new THREE.Mesh(armGeometry, bodyMaterial);
416
+ leftArm.position.set(-0.6, 1, 0);
417
+ leftArm.rotation.z = 0.5;
418
+ leftArm.castShadow = true;
419
+ group.add(leftArm);
420
+
421
+ const rightArm = new THREE.Mesh(armGeometry, bodyMaterial);
422
+ rightArm.position.set(0.6, 1, 0);
423
+ rightArm.rotation.z = -0.5;
424
+ rightArm.castShadow = true;
425
+ group.add(rightArm);
426
+
427
+ // Legs
428
+ const legGeometry = new THREE.CylinderGeometry(0.2, 0.2, 0.8, 6);
429
+ const leftLeg = new THREE.Mesh(legGeometry, new THREE.MeshStandardMaterial({ color: 0x1e40af }));
430
+ leftLeg.position.set(-0.2, -0.4, 0);
431
+ leftLeg.castShadow = true;
432
+ group.add(leftLeg);
433
+
434
+ const rightLeg = new THREE.Mesh(legGeometry, new THREE.MeshStandardMaterial({ color: 0x1e40af }));
435
+ rightLeg.position.set(0.2, -0.4, 0);
436
+ rightLeg.castShadow = true;
437
+ group.add(rightLeg);
438
+
439
+ group.position.y = 0;
440
+ scene.add(group);
441
+
442
+ gameState.player = {
443
+ model: group,
444
+ speed: 0.1,
445
+ runSpeed: 0.2,
446
+ rotationSpeed: 0.05,
447
+ isMoving: false,
448
+ animations: {
449
+ idle: null,
450
+ walk: null,
451
+ run: null,
452
+ jump: null
453
+ },
454
+ currentAnimation: null
455
+ };
456
+
457
+ // Add a simple animation mixer for the player
458
+ mixer = new THREE.AnimationMixer(group);
459
+
460
+ // Create simple animations
461
+ createPlayerAnimations();
462
+
463
+ // Set initial animation
464
+ setPlayerAnimation('idle');
465
+ }
466
+
467
+ function createPlayerAnimations() {
468
+ // In a real app, these would come from the GLTF model
469
+ // For this example, we'll create simple animations
470
+
471
+ // Idle animation (slight bounce)
472
+ const idleTrack = new THREE.VectorKeyframeTrack(
473
+ '.position',
474
+ [0, 0.5, 1],
475
+ [
476
+ 0, 0, 0, // Start position
477
+ 0, 0.05, 0, // Up position
478
+ 0, 0, 0 // Back to start
479
+ ]
480
+ );
481
+ const idleClip = new THREE.AnimationClip('idle', 1, [idleTrack]);
482
+ gameState.player.animations.idle = idleClip;
483
+
484
+ // Walk animation (arm and leg movement)
485
+ const leftArmTrack = new THREE.VectorKeyframeTrack(
486
+ '.children[2].rotation[z]',
487
+ [0, 0.5, 1],
488
+ [0.5, -0.5, 0.5]
489
+ );
490
+ const rightArmTrack = new THREE.VectorKeyframeTrack(
491
+ '.children[3].rotation[z]',
492
+ [0, 0.5, 1],
493
+ [-0.5, 0.5, -0.5]
494
+ );
495
+ const leftLegTrack = new THREE.VectorKeyframeTrack(
496
+ '.children[4].position[y]',
497
+ [0, 0.5, 1],
498
+ [-0.4, -0.2, -0.4]
499
+ );
500
+ const rightLegTrack = new THREE.VectorKeyframeTrack(
501
+ '.children[5].position[y]',
502
+ [0, 0.5, 1],
503
+ [-0.2, -0.4, -0.2]
504
+ );
505
+ const walkClip = new THREE.AnimationClip('walk', 0.5, [
506
+ leftArmTrack, rightArmTrack, leftLegTrack, rightLegTrack
507
+ ]);
508
+ gameState.player.animations.walk = walkClip;
509
+
510
+ // Run animation (faster arm and leg movement)
511
+ const runClip = new THREE.AnimationClip('run', 0.3, [
512
+ leftArmTrack, rightArmTrack, leftLegTrack, rightLegTrack
513
+ ]);
514
+ gameState.player.animations.run = runClip;
515
+
516
+ // Jump animation
517
+ const jumpTrack = new THREE.VectorKeyframeTrack(
518
+ '.position[y]',
519
+ [0, 0.2, 0.4, 0.6, 0.8, 1],
520
+ [0, 2, 1.5, 0.5, 0, 0]
521
+ );
522
+ const jumpClip = new THREE.AnimationClip('jump', 1, [jumpTrack]);
523
+ gameState.player.animations.jump = jumpClip;
524
+ }
525
+
526
+ function setPlayerAnimation(name) {
527
+ if (gameState.player.currentAnimation === name) return;
528
+
529
+ if (mixer) {
530
+ mixer.stopAllAction();
531
+
532
+ const clip = gameState.player.animations[name];
533
+ if (clip) {
534
+ const action = mixer.clipAction(clip);
535
+ action.setLoop(THREE.LoopRepeat);
536
+ action.play();
537
+ }
538
+
539
+ gameState.player.currentAnimation = name;
540
+ }
541
+ }
542
+
543
+ function loadNPCCharacters() {
544
+ gameState.selectedWorldCharacters.forEach((character, index) => {
545
+ // In a real app, we would load the GLTF model from the URL
546
+ // For this example, we'll create a simple placeholder
547
+
548
+ const group = new THREE.Group();
549
+
550
+ // Body
551
+ const bodyGeometry = new THREE.CylinderGeometry(0.5, 0.5, 1.5, 8);
552
+ const bodyMaterial = new THREE.MeshStandardMaterial({
553
+ color: new THREE.Color(character.color),
554
+ roughness: 0.7,
555
+ metalness: 0.1
556
+ });
557
+ const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
558
+ body.position.y = 0.75;
559
+ body.castShadow = true;
560
+ group.add(body);
561
+
562
+ // Head
563
+ const headGeometry = new THREE.SphereGeometry(0.4, 8, 8);
564
+ const headMaterial = new THREE.MeshStandardMaterial({ color: 0xf5d0b5 });
565
+ const head = new THREE.Mesh(headGeometry, headMaterial);
566
+ head.position.y = 1.6;
567
+ head.castShadow = true;
568
+ group.add(head);
569
+
570
+ // Position NPCs in a circle around the center
571
+ const angle = (index / gameState.selectedWorldCharacters.length) * Math.PI * 2;
572
+ const radius = 10 + Math.random() * 10;
573
+
574
+ group.position.x = Math.cos(angle) * radius;
575
+ group.position.z = Math.sin(angle) * radius;
576
+ group.position.y = 0;
577
+
578
+ // Make NPC face center
579
+ group.lookAt(0, 0, 0);
580
+
581
+ scene.add(group);
582
+
583
+ gameState.npcs.push({
584
+ model: group,
585
+ character: character,
586
+ dialog: character.dialog,
587
+ currentDialogIndex: 0
588
+ });
589
+ });
590
+ }
591
+
592
+ function animate() {
593
+ requestAnimationFrame(animate);
594
+
595
+ const delta = clock.getDelta();
596
+
597
+ // Update player animation mixer
598
+ if (mixer) {
599
+ mixer.update(delta);
600
+ }
601
+
602
+ // Handle player movement
603
+ handlePlayerMovement(delta);
604
+
605
+ // Check for nearby NPCs
606
+ checkForNearbyNPCs();
607
+
608
+ renderer.render(scene, camera);
609
+ }
610
+
611
+ function handlePlayerMovement(delta) {
612
+ if (!gameState.player) return;
613
+
614
+ const player = gameState.player;
615
+ let moving = false;
616
+
617
+ // Forward/backward movement
618
+ if (gameState.keys.w) {
619
+ player.model.translateZ(-player.speed * (gameState.keys.shift ? player.runSpeed / player.speed : 1));
620
+ moving = true;
621
+ }
622
+ if (gameState.keys.s) {
623
+ player.model.translateZ(player.speed);
624
+ moving = true;
625
+ }
626
+
627
+ // Left/right movement
628
+ if (gameState.keys.a) {
629
+ player.model.translateX(-player.speed);
630
+ moving = true;
631
+ }
632
+ if (gameState.keys.d) {
633
+ player.model.translateX(player.speed);
634
+ moving = true;
635
+ }
636
+
637
+ // Rotation
638
+ if (moving) {
639
+ // Calculate target rotation based on movement direction
640
+ const targetRotation = Math.atan2(
641
+ (gameState.keys.a ? -1 : 0) + (gameState.keys.d ? 1 : 0),
642
+ (gameState.keys.w ? -1 : 0) + (gameState.keys.s ? 1 : 0)
643
+ );
644
+
645
+ // Smoothly rotate towards target
646
+ player.model.rotation.y = THREE.MathUtils.lerp(
647
+ player.model.rotation.y,
648
+ targetRotation,
649
+ player.rotationSpeed
650
+ );
651
+ }
652
+
653
+ // Jumping
654
+ if (gameState.keys.space && !gameState.isJumping) {
655
+ gameState.isJumping = true;
656
+ setPlayerAnimation('jump');
657
+ setTimeout(() => {
658
+ gameState.isJumping = false;
659
+ updatePlayerAnimation();
660
+ }, 1000);
661
+
662
+ // Check if we're near an NPC to talk
663
+ if (gameState.nearbyNpc) {
664
+ showDialog(gameState.nearbyNpc);
665
+ }
666
+ }
667
+
668
+ // Update animation based on movement
669
+ if (moving !== player.isMoving || gameState.keys.shift) {
670
+ player.isMoving = moving;
671
+ updatePlayerAnimation();
672
+ }
673
+
674
+ // Update camera position to follow player
675
+ const cameraOffset = new THREE.Vector3(0, 5, 10);
676
+ cameraOffset.applyQuaternion(player.model.quaternion);
677
+ camera.position.copy(player.model.position.clone().add(cameraOffset));
678
+ camera.lookAt(player.model.position);
679
+ }
680
+
681
+ function updatePlayerAnimation() {
682
+ if (gameState.isJumping) return;
683
+
684
+ if (!gameState.player.isMoving) {
685
+ setPlayerAnimation('idle');
686
+ } else if (gameState.keys.shift) {
687
+ setPlayerAnimation('run');
688
+ } else {
689
+ setPlayerAnimation('walk');
690
+ }
691
+ }
692
+
693
+ function checkForNearbyNPCs() {
694
+ if (!gameState.player) return;
695
+
696
+ let closestNpc = null;
697
+ let closestDistance = Infinity;
698
+
699
+ gameState.npcs.forEach(npc => {
700
+ const distance = npc.model.position.distanceTo(gameState.player.model.position);
701
+ if (distance < 5 && distance < closestDistance) {
702
+ closestDistance = distance;
703
+ closestNpc = npc;
704
+ }
705
+ });
706
+
707
+ gameState.nearbyNpc = closestNpc;
708
+ }
709
+
710
+ function showDialog(npc) {
711
+ if (!npc) return;
712
+
713
+ // Get next dialog line
714
+ const dialog = npc.dialog[npc.currentDialogIndex];
715
+ npc.currentDialogIndex = (npc.currentDialogIndex + 1) % npc.dialog.length;
716
+
717
+ // Update dialog UI
718
+ document.getElementById('dialog-character').style.backgroundColor = npc.character.color;
719
+ document.getElementById('dialog-name').textContent = npc.character.name;
720
+ document.getElementById('dialog-text').textContent = dialog;
721
+
722
+ // Show dialog box
723
+ document.getElementById('dialog-box').classList.remove('translate-y-full');
724
+ }
725
+
726
+ function closeDialog() {
727
+ document.getElementById('dialog-box').classList.add('translate-y-full');
728
+ }
729
+
730
+ function handleKeyDown(event) {
731
+ switch (event.key.toLowerCase()) {
732
+ case 'w': gameState.keys.w = true; break;
733
+ case 'a': gameState.keys.a = true; break;
734
+ case 's': gameState.keys.s = true; break;
735
+ case 'd': gameState.keys.d = true; break;
736
+ case 'shift': gameState.keys.shift = true; break;
737
+ case ' ':
738
+ gameState.keys.space = true;
739
+ if (gameState.currentScreen === 'game' && gameState.nearbyNpc) {
740
+ showDialog(gameState.nearbyNpc);
741
+ }
742
+ break;
743
+ }
744
+ }
745
+
746
+ function handleKeyUp(event) {
747
+ switch (event.key.toLowerCase()) {
748
+ case 'w': gameState.keys.w = false; break;
749
+ case 'a': gameState.keys.a = false; break;
750
+ case 's': gameState.keys.s = false; break;
751
+ case 'd': gameState.keys.d = false; break;
752
+ case 'shift': gameState.keys.shift = false; break;
753
+ case ' ': gameState.keys.space = false; break;
754
+ }
755
+ }
756
+ </script>
757
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=KBLLR/character-selector" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
758
+ </html>
prompts.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ I want a vite project with threejs to select characters fro a 3d world that I can control with the keyboard for the character to run, walk, and jump. when the character gets close to another character a dialogue box opens by pressing spacebar next to them. So the App starts with a wellcome screen, then press button select character (the characters should be fetched from a google cloud bucket), then the ui asks you to select another 5 characters (also fetched) and then the generated world starts
2
+ can the intro have a canvas with a physics of floating glb (emoji001.glb up to emoji012.glb) concentring in the center but that when hovered with the mouse they move and spread while slowly return to center?