KwabsHug commited on
Commit
68f5a46
·
verified ·
1 Parent(s): f498f82

Upload 10 files

Browse files
.gitattributes CHANGED
@@ -35,3 +35,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  testmedia/Stable[[:space:]]Audio[[:space:]]-[[:space:]]Raindrops,[[:space:]]output.wav filter=lfs diff=lfs merge=lfs -text
37
  testmedia/SVD[[:space:]]-[[:space:]]random[[:space:]]squiggles[[:space:]]as[[:space:]]roads[[:space:]]video[[:space:]]004484.mp4 filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  testmedia/Stable[[:space:]]Audio[[:space:]]-[[:space:]]Raindrops,[[:space:]]output.wav filter=lfs diff=lfs merge=lfs -text
37
  testmedia/SVD[[:space:]]-[[:space:]]random[[:space:]]squiggles[[:space:]]as[[:space:]]roads[[:space:]]video[[:space:]]004484.mp4 filter=lfs diff=lfs merge=lfs -text
38
+ MovingDotSpaceExport/backgrounds/1stGameLoc123.png filter=lfs diff=lfs merge=lfs -text
39
+ MovingDotSpaceExport/backgrounds/2ndGameLoc123.png filter=lfs diff=lfs merge=lfs -text
40
+ MovingDotSpaceExport/backgrounds/ConstructionGameLoc.png filter=lfs diff=lfs merge=lfs -text
41
+ MovingDotSpaceExport/backgrounds/CyberpunkGameLoc.png filter=lfs diff=lfs merge=lfs -text
42
+ MovingDotSpaceExport/backgrounds/eldoria_background.png filter=lfs diff=lfs merge=lfs -text
43
+ MovingDotSpaceExport/backgrounds/MedievalFantasyGameLoc.png filter=lfs diff=lfs merge=lfs -text
44
+ MovingDotSpaceExport/backgrounds/SpaceOdysseyGameLoc.png filter=lfs diff=lfs merge=lfs -text
45
+ MovingDotSpaceExport/backgrounds/SuperTeacherGameLoc.png filter=lfs diff=lfs merge=lfs -text
MovingDotSpaceExport/MovingDotSpaceStandalone.html ADDED
@@ -0,0 +1,1014 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Moving Dot Space Game</title>
7
+ <style>
8
+ * {
9
+ box-sizing: border-box;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ body {
15
+ font-family: Arial, sans-serif;
16
+ background: #f0f0f0;
17
+ }
18
+
19
+ #game-root {
20
+ max-width: 100%;
21
+ overflow-x: hidden;
22
+ }
23
+
24
+ /* Theme Selector */
25
+ #theme-selector {
26
+ padding: 10px;
27
+ background: #fff;
28
+ border-bottom: 1px solid #ccc;
29
+ }
30
+
31
+ #theme-selector select {
32
+ padding: 5px 10px;
33
+ font-size: 14px;
34
+ }
35
+
36
+ #theme-selector button {
37
+ margin-left: 10px;
38
+ padding: 5px 10px;
39
+ cursor: pointer;
40
+ }
41
+
42
+ /* Game Container */
43
+ #game-container {
44
+ position: relative;
45
+ width: 100%;
46
+ height: 100vh;
47
+ border: 1px solid black;
48
+ overflow: hidden;
49
+ background-size: cover;
50
+ background-position: center;
51
+ background-color: #333;
52
+ }
53
+
54
+ /* Moving Dot (Player) */
55
+ #moving-dot {
56
+ position: absolute;
57
+ width: 20px;
58
+ height: 20px;
59
+ background-color: lightgreen;
60
+ border: 2px solid black;
61
+ cursor: pointer;
62
+ z-index: 10;
63
+ }
64
+
65
+ #moving-dot:focus {
66
+ outline: 3px solid yellow;
67
+ }
68
+
69
+ /* Targets */
70
+ .target {
71
+ position: absolute;
72
+ width: 20px;
73
+ height: 20px;
74
+ background-color: red;
75
+ }
76
+
77
+ .target-label {
78
+ position: absolute;
79
+ color: white;
80
+ font-size: 12px;
81
+ text-shadow: 1px 1px 2px black;
82
+ pointer-events: none;
83
+ white-space: nowrap;
84
+ }
85
+
86
+ /* Overlay Text (HUD) */
87
+ #overlay-text {
88
+ position: absolute;
89
+ top: 3%;
90
+ left: 3%;
91
+ color: black;
92
+ background-color: white;
93
+ font-weight: bold;
94
+ font-size: 15px;
95
+ border: 2px solid black;
96
+ padding: 5px 10px;
97
+ max-width: 90%;
98
+ }
99
+
100
+ /* Modal Overlay (shared) */
101
+ .modal-overlay {
102
+ position: fixed;
103
+ top: 0;
104
+ left: 0;
105
+ width: 100%;
106
+ height: 100%;
107
+ background-color: rgba(0, 0, 0, 0.5);
108
+ display: none;
109
+ justify-content: center;
110
+ align-items: center;
111
+ z-index: 1000;
112
+ }
113
+
114
+ .modal-overlay.open {
115
+ display: flex;
116
+ }
117
+
118
+ /* Modal Box */
119
+ .modal {
120
+ background-color: white;
121
+ border-radius: 4px;
122
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
123
+ max-width: 80%;
124
+ max-height: 80%;
125
+ overflow: auto;
126
+ min-width: 300px;
127
+ }
128
+
129
+ .modal-header {
130
+ display: flex;
131
+ justify-content: space-between;
132
+ align-items: center;
133
+ padding: 16px;
134
+ border-bottom: 1px solid #ddd;
135
+ }
136
+
137
+ .modal-header h2 {
138
+ margin: 0;
139
+ }
140
+
141
+ .modal-header button {
142
+ background: none;
143
+ border: none;
144
+ font-size: 24px;
145
+ cursor: pointer;
146
+ }
147
+
148
+ .modal-content {
149
+ padding: 16px;
150
+ }
151
+
152
+ .modal-content p {
153
+ margin-bottom: 15px;
154
+ }
155
+
156
+ .modal-actions {
157
+ display: flex;
158
+ flex-direction: column;
159
+ gap: 8px;
160
+ }
161
+
162
+ .modal-actions button {
163
+ padding: 10px 15px;
164
+ cursor: pointer;
165
+ background: #4a90d9;
166
+ color: white;
167
+ border: none;
168
+ border-radius: 4px;
169
+ }
170
+
171
+ .modal-actions button:hover {
172
+ background: #357abd;
173
+ }
174
+
175
+ .modal-image {
176
+ max-width: 100%;
177
+ margin-top: 10px;
178
+ }
179
+
180
+ /* Stats Panel */
181
+ #stats-panel {
182
+ border: 2px solid black;
183
+ display: grid;
184
+ grid-template-columns: repeat(4, 1fr);
185
+ gap: 10px;
186
+ padding: 10px;
187
+ background: #fff;
188
+ }
189
+
190
+ .stats-item {
191
+ border: 1px solid #ccc;
192
+ padding: 10px;
193
+ }
194
+
195
+ .stats-item h2, .stats-item h1 {
196
+ font-size: 16px;
197
+ margin-bottom: 10px;
198
+ }
199
+
200
+ .stats-item h6 {
201
+ font-size: 11px;
202
+ color: #666;
203
+ margin-bottom: 8px;
204
+ }
205
+
206
+ .stats-item ul {
207
+ list-style: none;
208
+ padding: 0;
209
+ }
210
+
211
+ .stats-item li {
212
+ margin: 5px 0;
213
+ }
214
+
215
+ .stats-item button {
216
+ margin: 3px;
217
+ padding: 5px 10px;
218
+ cursor: pointer;
219
+ }
220
+
221
+ /* Objectives styling */
222
+ #objectives-list .completed {
223
+ text-decoration: line-through;
224
+ color: green;
225
+ }
226
+
227
+ /* Color coding */
228
+ #stats-section { color: blue; }
229
+ #inventory-section { color: red; }
230
+ #skills-section { color: green; }
231
+
232
+ /* Responsive */
233
+ @media (max-width: 800px) {
234
+ #stats-panel {
235
+ grid-template-columns: repeat(2, 1fr);
236
+ }
237
+ }
238
+
239
+ @media (max-width: 500px) {
240
+ #stats-panel {
241
+ grid-template-columns: 1fr;
242
+ }
243
+ }
244
+ </style>
245
+ </head>
246
+ <body>
247
+ <div id="game-root">
248
+ <!-- Theme Selector -->
249
+ <div id="theme-selector">
250
+ Game Selector:
251
+ <select id="theme-dropdown"></select>
252
+ <button id="load-json-btn">Load gameConfig.json</button>
253
+ <button id="load-external-btn">Paste JSON Config</button>
254
+ <label style="margin-left:10px;">
255
+ Background: <input type="file" id="bg-file-input" accept="image/*" style="width:150px;">
256
+ </label>
257
+ </div>
258
+
259
+ <!-- Game Container -->
260
+ <div id="game-container">
261
+ <div id="overlay-text">Click game area and use arrow keys to move</div>
262
+ <div id="moving-dot" tabindex="0"></div>
263
+ <div id="targets-container"></div>
264
+ </div>
265
+
266
+ <!-- Simple Modal -->
267
+ <div id="simple-modal" class="modal-overlay">
268
+ <div class="modal">
269
+ <div class="modal-header">
270
+ <h2 id="simple-modal-title"></h2>
271
+ <button id="simple-modal-close">&times;</button>
272
+ </div>
273
+ <div class="modal-content">
274
+ <p id="simple-modal-content"></p>
275
+ <div id="simple-modal-actions" class="modal-actions"></div>
276
+ </div>
277
+ </div>
278
+ </div>
279
+
280
+ <!-- State Machine Modal -->
281
+ <div id="sm-modal" class="modal-overlay">
282
+ <div class="modal">
283
+ <div class="modal-header">
284
+ <h2 id="sm-modal-title"></h2>
285
+ <button id="sm-modal-close">&times;</button>
286
+ </div>
287
+ <div class="modal-content">
288
+ <p id="sm-modal-content"></p>
289
+ <div id="sm-modal-actions" class="modal-actions"></div>
290
+ <img id="sm-modal-image" class="modal-image" style="display:none;" alt="">
291
+ </div>
292
+ </div>
293
+ </div>
294
+
295
+ <!-- Stats Panel -->
296
+ <div id="stats-panel">
297
+ <div class="stats-item">
298
+ <h1>Objectives</h1>
299
+ <ul id="objectives-list"></ul>
300
+ </div>
301
+ <div class="stats-item" id="inventory-section">
302
+ <h2>Inventory</h2>
303
+ <h6>Affects movement and modal action options</h6>
304
+ <div id="inventory-list"></div>
305
+ </div>
306
+ <div class="stats-item" id="stats-section">
307
+ <h1>Player Stats</h1>
308
+ <h6>Affects Modal Interactions</h6>
309
+ <div id="player-stats"></div>
310
+ </div>
311
+ <div class="stats-item" id="skills-section">
312
+ <h2>Skill Tree / Abilities</h2>
313
+ <h6>Affects effectiveness of inventory</h6>
314
+ <ul id="skills-list"></ul>
315
+ </div>
316
+ </div>
317
+ </div>
318
+
319
+ <script>
320
+ // ===== GAME STATE =====
321
+ const gameState = {
322
+ dotPosition: { x: 900, y: 450 },
323
+ boundaries: { maxX: 1835, maxY: 890, minX: 0, minY: 0 },
324
+ step: 10,
325
+ currentTheme: 'Default',
326
+ currentThemeStage: 0,
327
+ inventory: [],
328
+ skills: [],
329
+ objectives: [],
330
+ targets: [],
331
+ persistentTargets: [],
332
+ playerStats: {
333
+ health: 100,
334
+ mana: 50,
335
+ strength: 10,
336
+ agility: 10,
337
+ intelligence: 10,
338
+ charisma: 10,
339
+ luck: 10,
340
+ money: 1000
341
+ },
342
+ // Modal state
343
+ isSimpleModalOpen: false,
344
+ isSMModalOpen: false,
345
+ currentSMModalStates: null,
346
+ currentSMModalState: 'default',
347
+ // Theme config
348
+ themes: {},
349
+ themeActions: {}
350
+ };
351
+
352
+ // ===== DOM REFERENCES =====
353
+ const elements = {
354
+ gameContainer: document.getElementById('game-container'),
355
+ movingDot: document.getElementById('moving-dot'),
356
+ targetsContainer: document.getElementById('targets-container'),
357
+ overlayText: document.getElementById('overlay-text'),
358
+ themeDropdown: document.getElementById('theme-dropdown'),
359
+ loadExternalBtn: document.getElementById('load-external-btn'),
360
+ // Simple Modal
361
+ simpleModal: document.getElementById('simple-modal'),
362
+ simpleModalTitle: document.getElementById('simple-modal-title'),
363
+ simpleModalContent: document.getElementById('simple-modal-content'),
364
+ simpleModalActions: document.getElementById('simple-modal-actions'),
365
+ simpleModalClose: document.getElementById('simple-modal-close'),
366
+ // State Machine Modal
367
+ smModal: document.getElementById('sm-modal'),
368
+ smModalTitle: document.getElementById('sm-modal-title'),
369
+ smModalContent: document.getElementById('sm-modal-content'),
370
+ smModalActions: document.getElementById('sm-modal-actions'),
371
+ smModalImage: document.getElementById('sm-modal-image'),
372
+ smModalClose: document.getElementById('sm-modal-close'),
373
+ // Stats
374
+ objectivesList: document.getElementById('objectives-list'),
375
+ inventoryList: document.getElementById('inventory-list'),
376
+ playerStats: document.getElementById('player-stats'),
377
+ skillsList: document.getElementById('skills-list')
378
+ };
379
+
380
+ // ===== MOVEMENT =====
381
+ function moveDot(newX, newY) {
382
+ const boundedX = Math.max(gameState.boundaries.minX, Math.min(newX, gameState.boundaries.maxX));
383
+ const boundedY = Math.max(gameState.boundaries.minY, Math.min(newY, gameState.boundaries.maxY));
384
+
385
+ gameState.dotPosition.x = boundedX;
386
+ gameState.dotPosition.y = boundedY;
387
+
388
+ renderDot();
389
+ checkCollisions();
390
+ updateOverlayText();
391
+ }
392
+
393
+ function handleKeyPress(e) {
394
+ if (gameState.isSimpleModalOpen || gameState.isSMModalOpen) return;
395
+
396
+ let newX = gameState.dotPosition.x;
397
+ let newY = gameState.dotPosition.y;
398
+
399
+ switch (e.key) {
400
+ case 'ArrowLeft': newX -= gameState.step; e.preventDefault(); break;
401
+ case 'ArrowRight': newX += gameState.step; e.preventDefault(); break;
402
+ case 'ArrowUp': newY -= gameState.step; e.preventDefault(); break;
403
+ case 'ArrowDown': newY += gameState.step; e.preventDefault(); break;
404
+ default: return;
405
+ }
406
+
407
+ moveDot(newX, newY);
408
+ }
409
+
410
+ // ===== COLLISION DETECTION =====
411
+ function checkCollisions() {
412
+ const dotPos = gameState.dotPosition;
413
+
414
+ gameState.targets.forEach(target => {
415
+ if (
416
+ dotPos.x < target.x + 10 &&
417
+ dotPos.x + 10 > target.x &&
418
+ dotPos.y < target.y + 10 &&
419
+ dotPos.y + 10 > target.y
420
+ ) {
421
+ handleCollision(target);
422
+ }
423
+ });
424
+ }
425
+
426
+ function handleCollision(target) {
427
+ // Handle subtargets notification
428
+ if (target.subtargets) {
429
+ alert("Subtargets found. Can be used as discovered locations.");
430
+ console.log(target.subtargets);
431
+ }
432
+
433
+ switch (target.collisionType) {
434
+ case "":
435
+ console.log("Nothing happened");
436
+ break;
437
+
438
+ case "alert":
439
+ alert(target.collisiontext);
440
+ break;
441
+
442
+ case "decision":
443
+ if (confirm(target.collisiontext)) {
444
+ alert("You said yes");
445
+ } else {
446
+ alert("You said no");
447
+ }
448
+ break;
449
+
450
+ case "modal":
451
+ openSimpleModal(target.modalConfig);
452
+ break;
453
+
454
+ case "smmodal":
455
+ openSMModal(target.modalStates);
456
+ break;
457
+
458
+ case "storypartchange":
459
+ changeStoryPart(target.transitionToPart);
460
+ break;
461
+
462
+ case "requirementsgated":
463
+ const currentStory = gameState.themes[gameState.currentTheme]?.story?.[gameState.currentThemeStage];
464
+ const objectives = currentStory?.objectives || [];
465
+ const allComplete = objectives.every(obj => obj.complete);
466
+
467
+ if (allComplete && (target.requirements || 0) > 5) {
468
+ alert(target.collisiontext);
469
+ } else {
470
+ alert("You have not journeyed enough to explore this area yet.");
471
+ }
472
+ break;
473
+
474
+ default:
475
+ console.log("Unknown collision type:", target.collisionType);
476
+ }
477
+ }
478
+
479
+ // ===== SIMPLE MODAL =====
480
+ function openSimpleModal(config) {
481
+ gameState.isSimpleModalOpen = true;
482
+
483
+ elements.simpleModalTitle.textContent = config.title || '';
484
+ elements.simpleModalContent.textContent = config.content || '';
485
+
486
+ // Render actions
487
+ elements.simpleModalActions.innerHTML = '';
488
+ (config.actions || []).forEach(action => {
489
+ const btn = document.createElement('button');
490
+ btn.textContent = action.label;
491
+ btn.addEventListener('click', () => {
492
+ closeSimpleModal();
493
+ executeAction(action.action);
494
+ });
495
+ elements.simpleModalActions.appendChild(btn);
496
+ });
497
+
498
+ elements.simpleModal.classList.add('open');
499
+ }
500
+
501
+ function closeSimpleModal() {
502
+ gameState.isSimpleModalOpen = false;
503
+ elements.simpleModal.classList.remove('open');
504
+ elements.movingDot.focus();
505
+ }
506
+
507
+ // ===== STATE MACHINE MODAL =====
508
+ function openSMModal(modalStates) {
509
+ gameState.isSMModalOpen = true;
510
+ gameState.currentSMModalStates = modalStates;
511
+
512
+ // Get first state key
513
+ const firstStateKey = Object.keys(modalStates)[0] || 'default';
514
+ gameState.currentSMModalState = firstStateKey;
515
+
516
+ renderSMModalState();
517
+ elements.smModal.classList.add('open');
518
+ }
519
+
520
+ function renderSMModalState() {
521
+ const states = gameState.currentSMModalStates;
522
+ const currentState = states[gameState.currentSMModalState];
523
+
524
+ if (!currentState) {
525
+ console.error("State not found:", gameState.currentSMModalState);
526
+ return;
527
+ }
528
+
529
+ elements.smModalTitle.textContent = currentState.title || '';
530
+ elements.smModalContent.textContent = currentState.content || '';
531
+
532
+ // Render actions
533
+ elements.smModalActions.innerHTML = '';
534
+ (currentState.items || []).forEach(item => {
535
+ const btn = document.createElement('button');
536
+ btn.textContent = item.label;
537
+ btn.addEventListener('click', () => handleSMModalAction(item));
538
+ elements.smModalActions.appendChild(btn);
539
+ });
540
+
541
+ // Handle image
542
+ if (currentState.modalimg) {
543
+ elements.smModalImage.src = currentState.modalimg;
544
+ elements.smModalImage.style.display = 'block';
545
+ } else {
546
+ elements.smModalImage.style.display = 'none';
547
+ }
548
+
549
+ // Execute consequences
550
+ (currentState.consequences || []).forEach(consequence => {
551
+ executeAction(consequence);
552
+ });
553
+ }
554
+
555
+ function handleSMModalAction(item) {
556
+ const action = item.action;
557
+
558
+ // Check if action is a state transition
559
+ if (gameState.currentSMModalStates[action]) {
560
+ gameState.currentSMModalState = action;
561
+ renderSMModalState();
562
+ } else {
563
+ // Execute as action and close
564
+ executeAction(action);
565
+ closeSMModal();
566
+ }
567
+ }
568
+
569
+ function closeSMModal() {
570
+ gameState.isSMModalOpen = false;
571
+ gameState.currentSMModalStates = null;
572
+ elements.smModal.classList.remove('open');
573
+ elements.movingDot.focus();
574
+ }
575
+
576
+ // ===== ACTIONS =====
577
+ function executeAction(actionName) {
578
+ if (gameState.themeActions && gameState.themeActions[actionName]) {
579
+ gameState.themeActions[actionName]();
580
+ } else {
581
+ console.log("Action executed:", actionName);
582
+ }
583
+ }
584
+
585
+ // ===== STORY PARTS =====
586
+ function changeStoryPart(partNumber) {
587
+ const theme = gameState.themes[gameState.currentTheme];
588
+ if (!theme) return;
589
+
590
+ gameState.currentThemeStage = partNumber;
591
+ const newStoryPart = theme.story?.find(part => part.part === partNumber);
592
+
593
+ if (newStoryPart) {
594
+ gameState.objectives = newStoryPart.objectives || [];
595
+ gameState.targets = [
596
+ ...(theme.persistentTargets || []),
597
+ ...(newStoryPart.targets || [])
598
+ ];
599
+ renderTargets();
600
+ renderStats();
601
+ }
602
+ }
603
+
604
+ // ===== THEME LOADING =====
605
+ function loadTheme(themeName) {
606
+ const theme = gameState.themes[themeName];
607
+ if (!theme) {
608
+ console.error("Theme not found:", themeName);
609
+ return;
610
+ }
611
+
612
+ gameState.currentTheme = themeName;
613
+ gameState.currentThemeStage = 0;
614
+
615
+ // Set background - supports: URLs, base64 data URIs, or solid colors (#hex/named)
616
+ if (theme.background) {
617
+ const bg = theme.background;
618
+ if (bg.startsWith('#') || bg.startsWith('rgb') || /^[a-z]+$/i.test(bg)) {
619
+ // Solid color
620
+ elements.gameContainer.style.backgroundImage = 'none';
621
+ elements.gameContainer.style.backgroundColor = bg;
622
+ } else {
623
+ // URL or base64
624
+ elements.gameContainer.style.backgroundImage = `url('${bg}')`;
625
+ elements.gameContainer.style.backgroundColor = theme.backgroundColor || '#333';
626
+ }
627
+ }
628
+
629
+ // Load inventory and skills
630
+ gameState.inventory = theme.inventory || [];
631
+ gameState.skills = theme.skills || [];
632
+ gameState.persistentTargets = theme.persistentTargets || [];
633
+
634
+ // Load actions
635
+ gameState.themeActions = theme.actions || {};
636
+
637
+ // Load initial story part
638
+ const initialStory = theme.story?.find(part => part.part === 0);
639
+ if (initialStory) {
640
+ gameState.objectives = initialStory.objectives || [];
641
+
642
+ // Combine persistent and story targets
643
+ const uniqueTargets = [
644
+ ...gameState.persistentTargets,
645
+ ...(initialStory.targets || []).filter(
646
+ target => !gameState.persistentTargets.some(pt => pt.name === target.name)
647
+ )
648
+ ];
649
+ gameState.targets = uniqueTargets;
650
+ } else {
651
+ gameState.objectives = [];
652
+ gameState.targets = [...gameState.persistentTargets];
653
+ }
654
+
655
+ renderAll();
656
+ }
657
+
658
+ // ===== RENDERING =====
659
+ function renderDot() {
660
+ elements.movingDot.style.left = gameState.dotPosition.x + 'px';
661
+ elements.movingDot.style.top = gameState.dotPosition.y + 'px';
662
+ }
663
+
664
+ function renderTargets() {
665
+ elements.targetsContainer.innerHTML = '';
666
+
667
+ gameState.targets.forEach(target => {
668
+ // Target box
669
+ const targetEl = document.createElement('div');
670
+ targetEl.className = 'target';
671
+ targetEl.style.left = target.x + 'px';
672
+ targetEl.style.top = target.y + 'px';
673
+ elements.targetsContainer.appendChild(targetEl);
674
+
675
+ // Label
676
+ const label = document.createElement('span');
677
+ label.className = 'target-label';
678
+ label.style.left = target.x + 'px';
679
+ label.style.top = (target.y - 15) + 'px';
680
+ label.textContent = target.name;
681
+ elements.targetsContainer.appendChild(label);
682
+ });
683
+ }
684
+
685
+ function renderStats() {
686
+ // Objectives
687
+ elements.objectivesList.innerHTML = '';
688
+ gameState.objectives.forEach(obj => {
689
+ const li = document.createElement('li');
690
+ li.className = obj.complete ? 'completed' : '';
691
+ li.textContent = `${obj.id}: ${obj.name}${obj.complete ? ' (Completed)' : ''}`;
692
+ elements.objectivesList.appendChild(li);
693
+ });
694
+
695
+ // Inventory
696
+ elements.inventoryList.innerHTML = '';
697
+ gameState.inventory.forEach(item => {
698
+ const btn = document.createElement('button');
699
+ btn.textContent = item.name;
700
+ btn.addEventListener('click', () => {
701
+ alert(`${item.name}\n${item.description}\nType: ${item.type}`);
702
+ });
703
+ elements.inventoryList.appendChild(btn);
704
+ });
705
+
706
+ // Player Stats
707
+ const stats = gameState.playerStats;
708
+ elements.playerStats.innerHTML = `
709
+ <div>Health: ${stats.health} | Mana: ${stats.mana} | Strength: ${stats.strength}</div>
710
+ <div>Agility: ${stats.agility} | Intelligence: ${stats.intelligence} | Charisma: ${stats.charisma}</div>
711
+ <div>Luck: ${stats.luck} | Money: ${stats.money}</div>
712
+ `;
713
+
714
+ // Skills
715
+ elements.skillsList.innerHTML = '';
716
+ gameState.skills.forEach(skill => {
717
+ const li = document.createElement('li');
718
+ li.textContent = `${skill.name}: ${skill.branch} - Learned: ${skill.learned ? 'Yes' : 'No'}`;
719
+ elements.skillsList.appendChild(li);
720
+ });
721
+ }
722
+
723
+ function updateOverlayText() {
724
+ elements.overlayText.textContent = `Position: ${gameState.dotPosition.x}, ${gameState.dotPosition.y} | Theme: ${gameState.currentTheme} | Stage: ${gameState.currentThemeStage}`;
725
+ }
726
+
727
+ function renderThemeDropdown() {
728
+ elements.themeDropdown.innerHTML = '';
729
+ Object.keys(gameState.themes).forEach(themeName => {
730
+ const option = document.createElement('option');
731
+ option.value = themeName;
732
+ option.textContent = themeName;
733
+ if (themeName === gameState.currentTheme) {
734
+ option.selected = true;
735
+ }
736
+ elements.themeDropdown.appendChild(option);
737
+ });
738
+ }
739
+
740
+ function renderAll() {
741
+ renderDot();
742
+ renderTargets();
743
+ renderStats();
744
+ renderThemeDropdown();
745
+ updateOverlayText();
746
+ }
747
+
748
+ // ===== INVENTORY HELPERS =====
749
+ function addInventoryItem(item) {
750
+ gameState.inventory.push(item);
751
+ renderStats();
752
+ }
753
+
754
+ function removeInventoryItem(itemName) {
755
+ gameState.inventory = gameState.inventory.filter(item => item.name !== itemName);
756
+ renderStats();
757
+ }
758
+
759
+ // ===== EVENT LISTENERS =====
760
+ function setupEventListeners() {
761
+ // Keyboard movement
762
+ elements.movingDot.addEventListener('keydown', handleKeyPress);
763
+
764
+ // Click to focus
765
+ elements.gameContainer.addEventListener('click', () => {
766
+ elements.movingDot.focus();
767
+ });
768
+
769
+ // Theme dropdown
770
+ elements.themeDropdown.addEventListener('change', (e) => {
771
+ loadTheme(e.target.value);
772
+ });
773
+
774
+ // Modal close buttons
775
+ elements.simpleModalClose.addEventListener('click', closeSimpleModal);
776
+ elements.smModalClose.addEventListener('click', closeSMModal);
777
+
778
+ // Click outside modal to close
779
+ elements.simpleModal.addEventListener('click', (e) => {
780
+ if (e.target === elements.simpleModal) closeSimpleModal();
781
+ });
782
+ elements.smModal.addEventListener('click', (e) => {
783
+ if (e.target === elements.smModal) closeSMModal();
784
+ });
785
+
786
+ // Load from gameConfig.json button
787
+ document.getElementById('load-json-btn').addEventListener('click', async () => {
788
+ try {
789
+ const response = await fetch('gameConfig.json');
790
+ if (!response.ok) throw new Error('gameConfig.json not found');
791
+ const config = await response.json();
792
+ initMovingDotSpace(config);
793
+ alert('Loaded ' + Object.keys(config.themes).length + ' themes from gameConfig.json');
794
+ } catch (e) {
795
+ alert("Error loading gameConfig.json: " + e.message);
796
+ }
797
+ });
798
+
799
+ // Paste external config button
800
+ elements.loadExternalBtn.addEventListener('click', () => {
801
+ const configStr = prompt("Paste JSON config:");
802
+ if (configStr) {
803
+ try {
804
+ const config = JSON.parse(configStr);
805
+ initMovingDotSpace(config);
806
+ } catch (e) {
807
+ alert("Invalid JSON: " + e.message);
808
+ }
809
+ }
810
+ });
811
+
812
+ // Background file input
813
+ const bgFileInput = document.getElementById('bg-file-input');
814
+ bgFileInput.addEventListener('change', async (e) => {
815
+ const file = e.target.files[0];
816
+ if (file) {
817
+ await setBackgroundFromFile(file);
818
+ }
819
+ });
820
+ }
821
+
822
+ // ===== GRADIO INTEGRATION =====
823
+ // This function accepts config from Gradio or external sources
824
+ function initMovingDotSpace(config) {
825
+ // Handle single theme or multi-theme config
826
+ if (config.themes) {
827
+ // Multi-theme config
828
+ gameState.themes = config.themes;
829
+ const firstTheme = Object.keys(config.themes)[0];
830
+ if (firstTheme) {
831
+ loadTheme(firstTheme);
832
+ }
833
+ } else if (config.background || config.story) {
834
+ // Single theme config - wrap it
835
+ gameState.themes = { 'Custom': config };
836
+ loadTheme('Custom');
837
+ }
838
+
839
+ renderThemeDropdown();
840
+ }
841
+
842
+ // ===== DEFAULT CONFIG (for standalone testing) =====
843
+ // Background can be: URL path, solid color (#hex or color name), or base64 data URI
844
+ // To use local images, place them in backgrounds/ folder and reference as "backgrounds/filename.png"
845
+ const defaultConfig = {
846
+ themes: {
847
+ 'Default': {
848
+ // Use local backgrounds folder - images should be in backgrounds/ subfolder
849
+ background: 'backgrounds/1stGameLoc123.png',
850
+ backgroundColor: '#2d5a27', // Fallback color if image doesn't load
851
+ inventory: [
852
+ { type: "book", name: "My Memories", description: "Currently I dont remember when I start this journey...." },
853
+ { type: "weapon", name: "Sword", description: "A sharp blade." },
854
+ { type: "armor", name: "Shield", description: "Protects against attacks." },
855
+ { type: "consumable", name: "Health Potion", description: "Restores health." }
856
+ ],
857
+ skills: [
858
+ { branch: "Combat", name: "Basic Attack", learned: false },
859
+ { branch: "Magic", name: "Fireball", learned: false },
860
+ { branch: "Stealth", name: "Sneak", learned: false }
861
+ ],
862
+ persistentTargets: [],
863
+ story: [
864
+ {
865
+ part: 0,
866
+ objectives: [
867
+ { id: "Main", name: "Explore the area", complete: false }
868
+ ],
869
+ targets: [
870
+ { name: "Target 1", x: 50, y: 50, collisionType: "alert", collisiontext: "You found Target 1!" },
871
+ { name: "Target 2", x: 100, y: 100, collisionType: "", collisiontext: "" },
872
+ { name: "State Machine Demo", x: 600, y: 600, collisionType: "smmodal", modalStates: {
873
+ default: {
874
+ title: "Entrance",
875
+ content: "You've reached the Entrance. What's your next step?",
876
+ items: [
877
+ { label: "Ask for guidance", action: "guidance" },
878
+ { label: "Buy an Axe", action: "buyAxe" }
879
+ ],
880
+ consequences: []
881
+ },
882
+ guidance: {
883
+ title: "Entrance - Guidance",
884
+ content: "The path to the north leads to the forest.",
885
+ items: [
886
+ { label: "Head North", action: "forest" },
887
+ { label: "Return", action: "default" }
888
+ ],
889
+ consequences: []
890
+ },
891
+ forest: {
892
+ title: "Forest",
893
+ content: "You went to the forest. Leaves blow everywhere.",
894
+ items: [],
895
+ consequences: ['gainleaf']
896
+ }
897
+ }},
898
+ { name: "Modal Demo", x: 300, y: 300, collisionType: "modal", modalConfig: {
899
+ title: "Simple Modal",
900
+ content: "This is a simple modal with action buttons.",
901
+ actions: [
902
+ { label: "Action 1", action: "action1" },
903
+ { label: "Action 2", action: "action2" }
904
+ ]
905
+ }},
906
+ { name: "Switch to Part 1", x: 700, y: 700, collisionType: "storypartchange", transitionToPart: 1 }
907
+ ]
908
+ },
909
+ {
910
+ part: 1,
911
+ objectives: [
912
+ { id: "Part2", name: "You made it to Part 1!", complete: false }
913
+ ],
914
+ targets: [
915
+ { name: "Part 1 Target", x: 50, y: 50, collisionType: "alert", collisiontext: "Welcome to Part 1!" },
916
+ { name: "Switch to Part 0", x: 600, y: 400, collisionType: "storypartchange", transitionToPart: 0 }
917
+ ]
918
+ }
919
+ ],
920
+ actions: {
921
+ action1: function() { alert("Action 1 executed!"); },
922
+ action2: function() { alert("Action 2 executed!"); },
923
+ buyAxe: function() {
924
+ alert("Bought an Axe!");
925
+ addInventoryItem({ type: "weapon", name: "Axe", description: "A sturdy axe." });
926
+ },
927
+ gainleaf: function() {
928
+ addInventoryItem({ type: "misc", name: "Leaf", description: "A random leaf blown by the wind." });
929
+ }
930
+ }
931
+ },
932
+ 'Space Odyssey': {
933
+ background: 'backgrounds/SpaceOdysseyGameLoc.png',
934
+ backgroundColor: '#0a1628', // Fallback
935
+ inventory: [
936
+ { type: "weapon", name: "Laser Gun", description: "A powerful laser weapon." },
937
+ { type: "tool", name: "Repair Kit", description: "A kit for repairs." }
938
+ ],
939
+ skills: [
940
+ { branch: "Piloting", name: "Astro Navigation", learned: false },
941
+ { branch: "Engineering", name: "Repair Mastery", learned: false }
942
+ ],
943
+ persistentTargets: [
944
+ { name: "Space Station", x: 200, y: 300, collisionType: "alert", collisiontext: "Welcome to the Space Station!" }
945
+ ],
946
+ story: [
947
+ {
948
+ part: 0,
949
+ objectives: [
950
+ { id: "Space", name: "Explore the cosmos", complete: false }
951
+ ],
952
+ targets: [
953
+ { name: "Alien Planet", x: 500, y: 400, collisionType: "alert", collisiontext: "An alien planet! The inhabitants seem cautious." },
954
+ { name: "Asteroid Field", x: 800, y: 500, collisionType: "decision", collisiontext: "Navigate through the asteroid field?" }
955
+ ]
956
+ }
957
+ ],
958
+ actions: {}
959
+ }
960
+ }
961
+ };
962
+
963
+ // ===== HELPER: Convert image file to base64 =====
964
+ // Use this in browser console to convert local images:
965
+ // loadImageAsBase64(file) - pass a File object from input
966
+ function loadImageAsBase64(file) {
967
+ return new Promise((resolve, reject) => {
968
+ const reader = new FileReader();
969
+ reader.onload = () => resolve(reader.result);
970
+ reader.onerror = reject;
971
+ reader.readAsDataURL(file);
972
+ });
973
+ }
974
+
975
+ // Helper to load image from file input and update current theme background
976
+ async function setBackgroundFromFile(file) {
977
+ const base64 = await loadImageAsBase64(file);
978
+ gameState.themes[gameState.currentTheme].background = base64;
979
+ elements.gameContainer.style.backgroundImage = `url('${base64}')`;
980
+ console.log("Background updated! Base64 length:", base64.length);
981
+ }
982
+
983
+ // Expose helpers
984
+ window.loadImageAsBase64 = loadImageAsBase64;
985
+ window.setBackgroundFromFile = setBackgroundFromFile;
986
+
987
+ // ===== INITIALIZATION =====
988
+ function init() {
989
+ setupEventListeners();
990
+
991
+ // Load default config
992
+ initMovingDotSpace(defaultConfig);
993
+
994
+ // Focus the dot
995
+ setTimeout(() => elements.movingDot.focus(), 100);
996
+ }
997
+
998
+ // Start on DOM ready
999
+ if (document.readyState === 'loading') {
1000
+ document.addEventListener('DOMContentLoaded', init);
1001
+ } else {
1002
+ init();
1003
+ }
1004
+
1005
+ // Expose for Gradio / external use
1006
+ window.initMovingDotSpace = initMovingDotSpace;
1007
+ window.gameState = gameState;
1008
+ window.addInventoryItem = addInventoryItem;
1009
+ window.removeInventoryItem = removeInventoryItem;
1010
+
1011
+ /* CONFIG_PLACEHOLDER */
1012
+ </script>
1013
+ </body>
1014
+ </html>
MovingDotSpaceExport/backgrounds/1stGameLoc123.png ADDED

Git LFS Details

  • SHA256: e412a9be5d9bd002d2927f7d7563e3b862bf783e5f0832d5a60675d5c0690736
  • Pointer size: 132 Bytes
  • Size of remote file: 4.43 MB
MovingDotSpaceExport/backgrounds/2ndGameLoc123.png ADDED

Git LFS Details

  • SHA256: 33359a4c6ac6d3b896b27a34231c98db3ad994bb14b3f32bca84f09f5543c484
  • Pointer size: 132 Bytes
  • Size of remote file: 3.5 MB
MovingDotSpaceExport/backgrounds/ConstructionGameLoc.png ADDED

Git LFS Details

  • SHA256: ea5cd0a1ce33e1a2f81a9f0fb20b4cb19a1583084709bef3b35979147c4a0eba
  • Pointer size: 131 Bytes
  • Size of remote file: 852 kB
MovingDotSpaceExport/backgrounds/CyberpunkGameLoc.png ADDED

Git LFS Details

  • SHA256: 3e247407a73a61e273252a867b765b840ac7c1df1af558be02acdfe5f5394e94
  • Pointer size: 131 Bytes
  • Size of remote file: 606 kB
MovingDotSpaceExport/backgrounds/MedievalFantasyGameLoc.png ADDED

Git LFS Details

  • SHA256: 598ca39414f0ac7256034b5d7ff97847b4d9788245d57c1deca67ddd5156089c
  • Pointer size: 131 Bytes
  • Size of remote file: 539 kB
MovingDotSpaceExport/backgrounds/SpaceOdysseyGameLoc.png ADDED

Git LFS Details

  • SHA256: 1e56704158c8dfc347bc124bb9ec568af9eb579086f08418b5e3ae02ea20a8d6
  • Pointer size: 131 Bytes
  • Size of remote file: 440 kB
MovingDotSpaceExport/backgrounds/SuperTeacherGameLoc.png ADDED

Git LFS Details

  • SHA256: cf36d7b99e418961664e41ffb46b0649064332b44b496c5fdb2d175a72cc077b
  • Pointer size: 131 Bytes
  • Size of remote file: 521 kB
MovingDotSpaceExport/backgrounds/eldoria_background.png ADDED

Git LFS Details

  • SHA256: e7924b97a780869c7f95c81153ee27846a202295248833c570365f912c8120c0
  • Pointer size: 131 Bytes
  • Size of remote file: 779 kB
MovingDotSpaceExport/gameConfig.json ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "themes": {
3
+ "User Custom - Template": {
4
+ "background": "backgrounds/theme_background.png",
5
+ "inventory": [
6
+ { "type": "weapon", "name": "Random weapon", "description": "A powerful weapon." }
7
+ ],
8
+ "skills": [
9
+ { "branch": "Skill Group 1", "name": "Skill One", "learned": false }
10
+ ],
11
+ "persistentTargets": [
12
+ { "name": "Background Target 1", "x": 500, "y": 500, "collisionType": "alert", "collisiontext": "First Test" }
13
+ ],
14
+ "story": [
15
+ {
16
+ "part": 0,
17
+ "objectives": [
18
+ { "id": 1, "name": "Mission Details", "complete": false, "progress": 0, "inventoryRequired": [] }
19
+ ],
20
+ "targets": [
21
+ { "name": "Target 1", "x": 150, "y": 150, "collisionType": "alert", "collisiontext": "First Test" }
22
+ ]
23
+ },
24
+ {
25
+ "part": 1,
26
+ "objectives": [
27
+ { "id": 2, "name": "Mission Details", "complete": false, "progress": 0, "inventoryRequired": [] }
28
+ ],
29
+ "targets": [
30
+ { "name": "Target 2", "x": 250, "y": 250, "collisionType": "alert", "collisiontext": "First Test" }
31
+ ]
32
+ }
33
+ ],
34
+ "actions": {}
35
+ },
36
+ "Default": {
37
+ "background": "backgrounds/1stGameLoc123.png",
38
+ "inventory": [
39
+ { "type": "book", "name": "My Memories", "description": "Currently I dont remember when I start this journey...." },
40
+ { "type": "weapon", "name": "Sword", "description": "A sharp blade." },
41
+ { "type": "armor", "name": "Shield", "description": "Protects against attacks." },
42
+ { "type": "consumable", "name": "Health Potion", "description": "Restores health." }
43
+ ],
44
+ "skills": [
45
+ { "branch": "Combat", "name": "Basic Attack", "learned": false },
46
+ { "branch": "Magic", "name": "Fireball", "learned": false },
47
+ { "branch": "Stealth", "name": "Sneak", "learned": false }
48
+ ],
49
+ "persistentTargets": [],
50
+ "story": [
51
+ {
52
+ "part": 0,
53
+ "objectives": [],
54
+ "targets": [
55
+ { "name": "Target 1", "x": 50, "y": 50, "collisionType": "alert", "collisiontext": "First Test",
56
+ "subtargets": [
57
+ { "name": "Target 1 sub 1", "x": 100, "y": 75, "collisionType": "alert", "collisiontext": "First Test in Sub of First Test" }
58
+ ]
59
+ },
60
+ { "name": "Target 2", "x": 100, "y": 100, "collisionType": "", "collisiontext": "" },
61
+ { "name": "Target 2 - SM Demo", "x": 600, "y": 600, "collisionType": "smmodal", "modalStates": {
62
+ "default": {
63
+ "title": "Entrance",
64
+ "content": "You've reached the Entrance. What's your next step?",
65
+ "items": [
66
+ { "label": "Ask for guidance on next move", "action": "guidance" },
67
+ { "label": "Buy an Axe", "action": "buyAxe" }
68
+ ],
69
+ "consequences": [],
70
+ "modalimg": "backgrounds/1stGameLoc123.png"
71
+ },
72
+ "guidance": {
73
+ "title": "Entrance - Guidance",
74
+ "content": "The path to the north leads to the forest, and the path to the south leads to the village.",
75
+ "items": [
76
+ { "label": "Head North", "action": "forest" },
77
+ { "label": "Return", "action": "default" }
78
+ ],
79
+ "consequences": []
80
+ },
81
+ "forest": {
82
+ "title": "Forest",
83
+ "content": "Went to the forest",
84
+ "items": [],
85
+ "consequences": ["gainleaf"]
86
+ }
87
+ }},
88
+ { "name": "Entrance", "x": 995, "y": 660, "collisionType": "modal", "modalConfig": {
89
+ "title": "Entrance",
90
+ "content": "You've reached the Entrance. What's your next step?",
91
+ "actions": [
92
+ { "label": "Ask for guidance on next move", "action": "askforDirections" },
93
+ { "label": "Buy an Axe", "action": "buyAxeAlert" }
94
+ ]
95
+ }},
96
+ { "name": "Market Stall", "x": 200, "y": 300, "collisionType": "", "collisiontext": "" },
97
+ { "name": "Inn Entrance", "x": 400, "y": 450, "collisionType": "", "collisiontext": "" },
98
+ { "name": "Town Hall", "x": 600, "y": 350, "collisionType": "", "collisiontext": "" },
99
+ { "name": "Fountain", "x": 500, "y": 500, "collisionType": "", "collisiontext": "" },
100
+ { "name": "Bridge", "x": 1100, "y": 700, "collisionType": "", "collisiontext": "" },
101
+ { "name": "Waterfall", "x": 1300, "y": 800, "collisionType": "", "collisiontext": "" },
102
+ { "name": "Mountain Peak", "x": 1500, "y": 100, "collisionType": "", "collisiontext": "" },
103
+ { "name": "Switch Test 1", "x": 700, "y": 700, "collisionType": "storypartchange", "collisiontext": "First Test", "transitionToPart": 1 }
104
+ ]
105
+ },
106
+ {
107
+ "part": 1,
108
+ "objectives": [],
109
+ "targets": [
110
+ { "name": "Target 1", "x": 50, "y": 50, "collisionType": "alert", "collisiontext": "First Test" },
111
+ { "name": "Switch Back Test 1", "x": 600, "y": 400, "collisionType": "storypartchange", "collisiontext": "First Test", "transitionToPart": 0 }
112
+ ]
113
+ }
114
+ ],
115
+ "actions": {}
116
+ },
117
+ "Space Odyssey": {
118
+ "background": "backgrounds/SpaceOdysseyGameLoc.png",
119
+ "inventory": [
120
+ { "type": "weapon", "name": "Laser Gun", "description": "A powerful laser weapon." },
121
+ { "type": "tool", "name": "Repair Kit", "description": "A kit for repairing various equipment." },
122
+ { "type": "tool", "name": "Mining Laser", "description": "A tool for extracting minerals from asteroids." }
123
+ ],
124
+ "skills": [
125
+ { "branch": "Piloting", "name": "Astro Navigation", "learned": false },
126
+ { "branch": "Engineering", "name": "Repair Mastery", "learned": false }
127
+ ],
128
+ "persistentTargets": [
129
+ { "name": "Space Station", "x": 200, "y": 300, "collisionType": "", "collisiontext": "" },
130
+ { "name": "Alien Planet", "x": 940, "y": 460, "collisionType": "alert", "collisiontext": "You have arrived at an alien planet. The inhabitants seem cautious but willing to negotiate." },
131
+ { "name": "Abandoned Spaceship", "x": 460, "y": 570, "collisionType": "alert", "collisiontext": "You have discovered an abandoned spaceship of ancient origin." },
132
+ { "name": "Colony", "x": 640, "y": 500, "collisionType": "modal", "modalConfig": {
133
+ "title": "Human Colony",
134
+ "content": "You have reached the human colony. Rest and prepare for your upcoming missions.",
135
+ "actions": [
136
+ { "label": "Request Mission Briefing", "action": "requestBriefing" },
137
+ { "label": "Purchase Upgrades", "action": "purchaseUpgrades" }
138
+ ]
139
+ }},
140
+ { "name": "Meteor Field", "x": 1100, "y": 470, "collisionType": "modal", "modalConfig": {
141
+ "title": "Dangerous Meteor Field",
142
+ "content": "You have entered a dense meteor field. Navigate carefully.",
143
+ "actions": [
144
+ { "label": "Activate Shields", "action": "activateShields" },
145
+ { "label": "Scan for Rare Minerals", "action": "scanMinerals" }
146
+ ]
147
+ }}
148
+ ],
149
+ "story": [
150
+ {
151
+ "part": 0,
152
+ "objectives": [
153
+ { "id": 1, "name": "Dock at the Space Station", "complete": false, "progress": 0, "inventoryRequired": [] },
154
+ { "id": 2, "name": "Repair the Communication Array", "complete": false, "progress": 0, "inventoryRequired": ["Repair Kit"] }
155
+ ],
156
+ "targets": [
157
+ { "name": "Space Station Docking Bay", "x": 500, "y": 400, "collisionType": "alert", "collisiontext": "Welcome to the space station." },
158
+ { "name": "Communication Room", "x": 800, "y": 200, "collisionType": "alert", "collisiontext": "The communication array needs repair." },
159
+ { "name": "To Asteroid Field", "x": 900, "y": 100, "collisionType": "storypartchange", "transitionToPart": 1, "collisiontext": "Proceed to investigate the asteroid field." }
160
+ ]
161
+ },
162
+ {
163
+ "part": 1,
164
+ "objectives": [
165
+ { "id": 3, "name": "Collect Rare Asteroid Minerals", "complete": false, "progress": 0, "inventoryRequired": ["Mining Laser"] }
166
+ ],
167
+ "targets": [
168
+ { "name": "Asteroid Field", "x": 1200, "y": 300, "collisionType": "alert", "collisiontext": "Scan asteroids for rare minerals." },
169
+ { "name": "Alien Encounter", "x": 1250, "y": 350, "collisionType": "alert", "collisiontext": "An alien ship approaches." },
170
+ { "name": "To Alien Homeworld", "x": 1100, "y": 200, "collisionType": "storypartchange", "transitionToPart": 2, "collisiontext": "Travel to the alien homeworld." }
171
+ ]
172
+ },
173
+ {
174
+ "part": 2,
175
+ "objectives": [
176
+ { "id": 4, "name": "Negotiate Peace with the Aliens", "complete": false, "progress": 0, "inventoryRequired": [] },
177
+ { "id": 5, "name": "Gather Alien Artifacts", "complete": false, "progress": 0, "inventoryRequired": ["Artifact Scanner"] }
178
+ ],
179
+ "targets": [
180
+ { "name": "Alien Council Chamber", "x": 600, "y": 250, "collisionType": "modal", "modalConfig": {
181
+ "title": "Alien Council Chamber",
182
+ "content": "Present your case for peace.",
183
+ "actions": [
184
+ { "label": "Present Peace Offer", "action": "presentPeaceOffer" },
185
+ { "label": "Listen to Alien Concerns", "action": "listenConcerns" }
186
+ ]
187
+ }},
188
+ { "name": "Alien Ruins", "x": 750, "y": 400, "collisionType": "alert", "collisiontext": "Ancient alien ruins discovered." },
189
+ { "name": "Return to Human Colony", "x": 800, "y": 150, "collisionType": "storypartchange", "transitionToPart": 3, "collisiontext": "Return to share findings." }
190
+ ]
191
+ }
192
+ ],
193
+ "actions": {}
194
+ },
195
+ "Medieval Fantasy": {
196
+ "background": "backgrounds/MedievalFantasyGameLoc.png",
197
+ "inventory": [
198
+ { "type": "weapon", "name": "Longsword", "description": "A sturdy steel blade." }
199
+ ],
200
+ "skills": [
201
+ { "branch": "Piloting", "name": "Astro Navigation", "learned": false }
202
+ ],
203
+ "persistentTargets": [
204
+ { "name": "Market Stall", "x": 200, "y": 300, "collisionType": "", "collisiontext": "" },
205
+ { "name": "Inn Entrance", "x": 400, "y": 450, "collisionType": "", "collisiontext": "" },
206
+ { "name": "Castle", "x": 940, "y": 460, "collisionType": "alert", "collisiontext": "The King summons you to find and release his champion." },
207
+ { "name": "Hidden Space", "x": 460, "y": 570, "collisionType": "alert", "collisiontext": "A hidden space. Nobody is here." },
208
+ { "name": "Home", "x": 640, "y": 500, "collisionType": "modal", "modalConfig": {
209
+ "title": "Your Space",
210
+ "content": "Rest and prepare for your tasks.",
211
+ "actions": [
212
+ { "label": "Ask for guidance", "action": "askforDirections" },
213
+ { "label": "Buy an Axe", "action": "buyAxeAlert" }
214
+ ]
215
+ }},
216
+ { "name": "Marketplace", "x": 1100, "y": 470, "collisionType": "modal", "modalConfig": {
217
+ "title": "Marketplace",
218
+ "content": "A bustling marketplace.",
219
+ "actions": [
220
+ { "label": "Ask for directions", "action": "askforDirections" },
221
+ { "label": "Buy an Axe", "action": "buyAxeAlert" },
222
+ { "label": "Ask about the knight", "action": "knightalert" }
223
+ ]
224
+ }},
225
+ { "name": "Mini Waterfall", "x": 500, "y": 640, "collisionType": "alert", "collisiontext": "A calming waterfall. There's a trail of water leading nearby." }
226
+ ],
227
+ "story": [
228
+ {
229
+ "part": 0,
230
+ "objectives": [
231
+ { "id": 1, "name": "Investigate the Disappearances", "complete": false, "progress": 0, "inventoryRequired": [] },
232
+ { "id": 6, "name": "Rescue the Captured Knight", "complete": false, "progress": 0, "inventoryRequired": ["Longsword"] }
233
+ ],
234
+ "targets": [
235
+ { "name": "Village Elder's House", "x": 500, "y": 400, "collisionType": "alert", "collisiontext": "The village needs your help investigating disappearances." },
236
+ { "name": "Mysterious Forest", "x": 800, "y": 200, "collisionType": "alert", "collisiontext": "An eerie silence envelops the forest." },
237
+ { "name": "To Goblin Bandits", "x": 900, "y": 100, "collisionType": "storypartchange", "transitionToPart": 1, "collisiontext": "Follow the tracks deeper into the forest." }
238
+ ]
239
+ },
240
+ {
241
+ "part": 1,
242
+ "objectives": [
243
+ { "id": 2, "name": "Defeat the Goblin Bandits", "complete": false, "progress": 0, "inventoryRequired": ["Longsword"] }
244
+ ],
245
+ "targets": [
246
+ { "name": "Bandit Camp", "x": 1200, "y": 300, "collisionType": "alert", "collisiontext": "Goblins attack! Prepare for battle!" },
247
+ { "name": "Prisoner Cages", "x": 1250, "y": 350, "collisionType": "alert", "collisiontext": "You free the villagers. They mention a sorcerer." },
248
+ { "name": "To Gather Information", "x": 1100, "y": 200, "collisionType": "storypartchange", "transitionToPart": 2, "collisiontext": "Gather information about the sorcerer." }
249
+ ]
250
+ }
251
+ ],
252
+ "actions": {}
253
+ },
254
+ "Cyberpunk": {
255
+ "background": "backgrounds/CyberpunkGameLoc.png",
256
+ "inventory": [
257
+ { "type": "weapon", "name": "Plasma Rifle", "description": "A high-tech firearm with plasma rounds." },
258
+ { "type": "armor", "name": "NanoSuit", "description": "Protects with reactive nano technology." },
259
+ { "type": "consumable", "name": "Stim Pack", "description": "Enhances reflexes temporarily." }
260
+ ],
261
+ "skills": [
262
+ { "branch": "Hacking", "name": "Cyber Intrusion", "learned": false },
263
+ { "branch": "Combat", "name": "Gun Kata", "learned": false },
264
+ { "branch": "Stealth", "name": "Cloaking", "learned": false }
265
+ ],
266
+ "persistentTargets": [],
267
+ "story": [
268
+ {
269
+ "part": 0,
270
+ "objectives": [],
271
+ "targets": [
272
+ { "name": "Target 1", "x": 50, "y": 50, "collisionType": "alert", "collisiontext": "First Test" },
273
+ { "name": "MegaCorp Server", "x": 200, "y": 50, "collisionType": "alert", "collisiontext": "Corporate server detected" },
274
+ { "name": "City Police", "x": 50, "y": 250, "collisionType": "alert", "collisiontext": "Law enforcement nearby" },
275
+ { "name": "Rival Gang", "x": 550, "y": 550, "collisionType": "alert", "collisiontext": "Gang territory" }
276
+ ]
277
+ }
278
+ ],
279
+ "actions": {}
280
+ },
281
+ "Super Teacher": {
282
+ "background": "backgrounds/SuperTeacherGameLoc.png",
283
+ "inventory": [
284
+ { "type": "book", "name": "Math Advice", "description": "Useful topical knowledge." },
285
+ { "type": "book", "name": "Science Advice", "description": "Useful topical knowledge." },
286
+ { "type": "book", "name": "English Advice", "description": "Useful topical knowledge." },
287
+ { "type": "book", "name": "Economics Advice", "description": "Useful topical knowledge." }
288
+ ],
289
+ "skills": [
290
+ { "branch": "Multitask", "name": "Movement Speed", "learned": false },
291
+ { "branch": "Multitask", "name": "Stop Window Interference for 1 min", "learned": false }
292
+ ],
293
+ "persistentTargets": [
294
+ { "name": "Super Teacher Toolkit", "x": 1000, "y": 330, "collisionType": "alert", "collisiontext": "Teaching tools available" }
295
+ ],
296
+ "story": [
297
+ {
298
+ "part": 0,
299
+ "objectives": [],
300
+ "targets": [
301
+ { "name": "Random Noise Interference", "x": 250, "y": 110, "collisionType": "alert", "collisiontext": "Distraction detected" },
302
+ { "name": "Student 1", "x": 310, "y": 620, "collisionType": "alert", "collisiontext": "Needs help with English" },
303
+ { "name": "Student 2", "x": 660, "y": 610, "collisionType": "alert", "collisiontext": "Doesn't speak English" },
304
+ { "name": "Student 3", "x": 1010, "y": 620, "collisionType": "alert", "collisiontext": "Needs help with Math" }
305
+ ]
306
+ }
307
+ ],
308
+ "actions": {}
309
+ },
310
+ "Fantasy Adventure": {
311
+ "background": "backgrounds/eldoria_background.png",
312
+ "inventory": [
313
+ { "type": "book", "name": "Book of Eldrak", "description": "An ancient tome with half a map." },
314
+ { "type": "amulet", "name": "Peculiar Amulet", "description": "Reveals the unseen." },
315
+ { "type": "map", "name": "Tattered Map", "description": "Hints at Whispering Woods locations." },
316
+ { "type": "artifact", "name": "Magical Acorn", "description": "Gift from the forest spirit." },
317
+ { "type": "relic", "name": "Shrine Relic", "description": "Enhances magical abilities." },
318
+ { "type": "record", "name": "Echoing Cave Echoes", "description": "Reveals secrets and hidden paths." }
319
+ ],
320
+ "skills": [
321
+ { "branch": "Combat", "name": "Sword Mastery", "learned": false },
322
+ { "branch": "Magic", "name": "Elemental Control", "learned": false },
323
+ { "branch": "Lore", "name": "Ancient Lore", "learned": true },
324
+ { "branch": "Stealth", "name": "Eavesdropping", "learned": true },
325
+ { "branch": "Stealth", "name": "Stealth Movement", "learned": true },
326
+ { "branch": "Diplomacy", "name": "Negotiation", "learned": true },
327
+ { "branch": "Magic", "name": "Artifact Mastery", "learned": true }
328
+ ],
329
+ "persistentTargets": [
330
+ { "name": "Lila's Home", "x": 460, "y": 600, "collisionType": "alert", "collisiontext": "A cozy cottage where the quest begins." },
331
+ { "name": "Eldoria Library", "x": 360, "y": 620, "collisionType": "alert", "collisiontext": "A treasure trove of books and maps." }
332
+ ],
333
+ "story": [
334
+ {
335
+ "part": 0,
336
+ "objectives": [],
337
+ "targets": [
338
+ { "name": "Eldoria Main Square", "x": 410, "y": 590, "collisionType": "requirementsgated", "collisiontext": "The heart of Eldoria.", "requirements": 8 },
339
+ { "name": "Whispering Forest Edge", "x": 830, "y": 700, "collisionType": "alert", "collisiontext": "The mysterious forest borders Eldoria." },
340
+ { "name": "Serene Hills", "x": 520, "y": 490, "collisionType": "alert", "collisiontext": "Gentle hills promise adventure." },
341
+ { "name": "Marketplace", "x": 360, "y": 560, "collisionType": "alert", "collisiontext": "Rumors of ancient artifacts." },
342
+ { "name": "Eldoria", "x": 490, "y": 420, "collisionType": "storypartchange", "collisiontext": "Your journey begins.", "transitionToPart": 1 },
343
+ { "name": "Old Sage's Hut", "x": 600, "y": 480, "collisionType": "alert", "collisiontext": "Seek the sage's wisdom." }
344
+ ]
345
+ },
346
+ {
347
+ "part": 1,
348
+ "objectives": [],
349
+ "targets": [
350
+ { "name": "Library of Eldrak", "x": 620, "y": 600, "collisionType": "alert", "collisiontext": "You discover the ancient Book of Eldrak." },
351
+ { "name": "Meridia Market", "x": 750, "y": 590, "collisionType": "alert", "collisiontext": "A peculiar amulet catches your eye." },
352
+ { "name": "Suspicious Place", "x": 810, "y": 530, "collisionType": "decision", "collisiontext": "Do you know why you came here?" },
353
+ { "name": "The Ancient Oak", "x": 300, "y": 130, "collisionType": "modal", "modalConfig": {
354
+ "title": "The Ancient Oak Area",
355
+ "content": "The Ancient Oak stands before you.",
356
+ "actions": [
357
+ { "label": "Talk to the Ancient Oak", "action": "approachAncientOak" }
358
+ ]
359
+ }},
360
+ { "name": "Magical Acorn", "x": 400, "y": 140, "collisionType": "modal", "modalConfig": {
361
+ "title": "Magical Acorn Area",
362
+ "content": "A spirit appears as you approach the acorn.",
363
+ "actions": [
364
+ { "label": "Talk to the spirit", "action": "acquireMagicalAcorn" }
365
+ ]
366
+ }}
367
+ ]
368
+ }
369
+ ],
370
+ "actions": {}
371
+ },
372
+ "Construction": {
373
+ "background": "backgrounds/ConstructionGameLoc.png",
374
+ "inventory": [
375
+ { "type": "tool", "name": "Hammer", "description": "A sturdy hammer for construction." },
376
+ { "type": "tool", "name": "Screwdriver", "description": "For fastening screws." },
377
+ { "type": "equipment", "name": "Safety Helmet", "description": "Protects from falling objects." },
378
+ { "type": "equipment", "name": "Safety Vest", "description": "High-visibility vest." },
379
+ { "type": "consumable", "name": "Energy Bar", "description": "Provides energy." }
380
+ ],
381
+ "skills": [
382
+ { "branch": "Construction", "name": "Electrical Wiring", "learned": true },
383
+ { "branch": "Construction", "name": "Concrete Pouring", "learned": true },
384
+ { "branch": "Safety", "name": "Fall Protection", "learned": false },
385
+ { "branch": "Safety", "name": "Hazard Recognition", "learned": true },
386
+ { "branch": "Equipment Operation", "name": "Forklift Certification", "learned": false }
387
+ ],
388
+ "persistentTargets": [
389
+ { "name": "Blueprints", "x": 300, "y": 670, "collisionType": "alert", "collisiontext": "Consult the blueprints." },
390
+ { "name": "Water Cooler", "x": 490, "y": 650, "collisionType": "alert", "collisiontext": "Take a break and hydrate." }
391
+ ],
392
+ "story": [
393
+ {
394
+ "part": 0,
395
+ "objectives": [
396
+ { "id": "Objective1_1", "name": "Attend Morning Briefing", "complete": false },
397
+ { "id": "Objective1_2", "name": "Gather Required Tools", "complete": false }
398
+ ],
399
+ "targets": [
400
+ { "name": "Construction Site", "x": 470, "y": 310, "collisionType": "alert", "collisiontext": "You arrive at the construction site." },
401
+ { "name": "Foreman's Office", "x": 1270, "y": 690, "collisionType": "alert", "collisiontext": "Morning briefing with the foreman." },
402
+ { "name": "Tool Shed", "x": 430, "y": 540, "collisionType": "alert", "collisiontext": "Gather your tools." },
403
+ { "name": "Continue Construction", "x": 1100, "y": 520, "collisionType": "storypartchange", "transitionToPart": 1, "collisiontext": "Continue to tasks." }
404
+ ]
405
+ },
406
+ {
407
+ "part": 1,
408
+ "objectives": [
409
+ { "id": "Objective2_1", "name": "Install Electrical Wiring", "complete": false },
410
+ { "id": "Objective2_2", "name": "Assist with Concrete Pouring", "complete": false },
411
+ { "id": "Objective2_3", "name": "Attend Safety Training", "complete": false }
412
+ ],
413
+ "targets": [
414
+ { "name": "Electrical Room", "x": 37, "y": 13, "collisionType": "alert", "collisiontext": "Install electrical wiring." },
415
+ { "name": "Foundation Area", "x": 370, "y": 50, "collisionType": "alert", "collisiontext": "Pour concrete for the foundation." },
416
+ { "name": "Safety Meeting Point", "x": 530, "y": 160, "collisionType": "alert", "collisiontext": "Safety training session." },
417
+ { "name": "Lunch Break Area", "x": 820, "y": 540, "collisionType": "alert", "collisiontext": "Lunch break time." }
418
+ ]
419
+ },
420
+ {
421
+ "part": 2,
422
+ "objectives": [
423
+ { "id": "Objective3_1", "name": "Inspect Completed Work", "complete": false },
424
+ { "id": "Objective3_2", "name": "Clean Up and Secure Site", "complete": false }
425
+ ],
426
+ "targets": [
427
+ { "name": "Inspection Point", "x": 90, "y": 600, "collisionType": "alert", "collisiontext": "Inspect your work." },
428
+ { "name": "Equipment Storage", "x": 220, "y": 800, "collisionType": "alert", "collisiontext": "Store your equipment." },
429
+ { "name": "Site Exit", "x": 900, "y": 900, "collisionType": "alert", "collisiontext": "End of day. Good work!" }
430
+ ]
431
+ }
432
+ ],
433
+ "actions": {}
434
+ }
435
+ }
436
+ }